DEV Community

An operation-oriented API using PHP and Symfony

Nacho Colomina Torregrosa on June 17, 2024

Introduction This post is a brief description about Practical PHP APIs with Symfony that I have recently published in Leanpub When deve...
Collapse
 
xwero profile image
david duymelinck

After reading the post and the comments I don't see the real benefit.

It looks you are pointing out that it is required to have a one fragment url in the post. But in the comments you acknowledge multi fragments are ok. It is true /send-payments or a variation is ambiguous. But an url like /account/{id}/send-payment gives all the context and you only need to and amount as payload.

You say there is no limit to operations. But where is the limit in making urls? Both things do the same, creating a hook to do things

I see the benefit that the payload gets fast in the system. But why would you want to add all the throttling, permissions, and so on to one controller instead of multiple. This could be a mess very soon if it doesn't get handled correctly.

A thing developers don't like to do is write documentation, and with that one endpoint you need to write all the cases. If you have multiple endpoints, you are already writing documentation with the code.

While it feels like a great way of working. My opinion is that the benefits are too shallow to make a valid way of working.

Collapse
 
icolomina profile image
Nacho Colomina Torregrosa

Hey David, thanks for your comments.
This approach can be beneficial if we combine it with the traditional approach. For instance, we could have a controller with the following routes:

  • POST /account
  • GET /account
  • GET /account/{id}
  • PATCH /account/{id}
  • DELETE /account/{id}

And then, form more complex operations we could use an operation-oriented approach,

  • POST /account/operation

With the last endpoint, we could handle operations such as send-payment, approve-order, sync-to-x etc. This operations could be organized within a collection using Symfony features. I think this can help to reduce the number of endpoints within the controllers and have them better organized . Crud operations would be handled as always and non crud operations would be handled with an operation-oriented approach.

With respect to "You say there is no limit to operations. But where is the limit in making urls? Both things do the same, creating a hook to do things": Yes, you are right, there is no limit in making urls. I think it's a matter of preference., I like organizing complex operations as services instead of having many endpoints.

With respect to: "But why would you want to add all the throttling, permissions, and so on to one controller instead of multiple. This could be a mess very soon if it doesn't get handled correctly.": No, i don't agree:

  • Is really easy to link voters with operations (or operations groups ) where the attribute would be "PERFORM" and the subject the operation.
  • Do not need to be one controller, for instance:
    • /account/{id}/operation
    • /template/{id}/operation Chapter 5 shows how to combine crud endpoints with operation-oriented ones and how to limit operations within controllers. For instance account only could execute "Account operations".
  • Throttling could be a problem if the collection loads a lot of operations but this problem could be handled by autowiring operations as lazy (all of them or only the heaviest).

With respect to: "A thing developers don't like to do is write documentation, and with that one endpoint you need to write all the cases. If you have multiple endpoints, you are already writing documentation with the code." : Yes, I can't argue with that. This approach would require documenting each operation separately.

Again, many thanks for your comments. For me its fantastic having developers feedback :)

Collapse
 
egorivanov profile image
Egor Ivanov

Thank you for the article. But why not
ApproveOrder — POST /orders/id/approve
SendPayment — POST /payments (send payment = create payment)
SyncData — needs more context to give an alternative example :)

Collapse
 
icolomina profile image
Nacho Colomina Torregrosa

Hey, Thank you for your comments:

  • POST /orders/id/approve: It's completly valid. It's simply that this article approach consider the "operation" as the main resource so that we can send "performing requests" to the operation endpoints. This gives flexibility for creating operation names and reduce the number of endpoints.

  • POST /payments: Can be ambiguous since does not specify exactly whether the call creates a new element on the payment resource or it sends the payment.

  • SyncData: Similar to approve. Let's imagine we have two systems A and B and we have to sync user data from A to B. We could:

    • POST /user/id/sync
    • POST /operation (Name in payload could be SyncToB)

Both approaches are valid, but, the operation-oriented way allows us to be more descriptive in a cleaner way:

  • POST: /user/id/sync-to-b
  • POST /operation (Name in payload could be SyncToB)

The second endpoint can play with the operation names leaving the url simple and readable.

I would like to emphasize that I have not written this book to present an operation-oriented approach as an alternative to other approaches, but as another option that can be applied by developers if it suits the needs of their project.

Thanks again for your comments :)

Collapse
 
jomisacu profile image
jomisacu

Very nice approach. I prefer buses with commands and command handlers instead, but this is a very clean and maintainable way.

Collapse
 
icolomina profile image
Nacho Colomina Torregrosa

Thanks for your comments!
"buses with commands and command handlers instead" -- You mean CQRS, don't you ?

Collapse
 
jomisacu profile image
jomisacu

Yes, of course. The thing is that you encapsulate all operations/use-cases/actions into a dedicated class which its unique purpose is to perform the related functionality. Even when you don't have CQRS this way is very good. I worked on projects without buses but commands and handlers.

Thread Thread
 
icolomina profile image
Nacho Colomina Torregrosa • Edited

Great! I will investigate more about CQRS so i would like to write a post comparing my approach wit CQRS.
Thanks again!

Collapse
 
nurie_creator profile image
IKE PAZ Nurie Creator

Perfect

Collapse
 
shjordan profile image
Jordan Humberto de Souza

Great article, would like to see a similar approach on Laravel.

Collapse
 
icolomina profile image
Nacho Colomina Torregrosa

Hey, thank you. I am not a regular Laravel user but i will study it and try to write an article for a Laravel aproach. Thanks again!