odan / slim4-skeleton

A Slim 4 Skeleton
https://odan.github.io/slim4-skeleton/
MIT License
439 stars 80 forks source link

I need tips on the ADR pattern for programming best practices #70

Closed esallum-iluminare closed 3 years ago

esallum-iluminare commented 3 years ago

Hi Odan, I want to thank you immensely for this SLIM 4 skeleton that you provided to the community. However, I have a beginner's doubt:

By the ADR development pattern, let's assume I have a domain called Categories and another one called Subcategories with their respective Actions (following your SLIM 4 skeleton pattern). The Categories action returns the categories and the Subcategories action returns the subcategories for their respective routes.

However, I need a route that returns the subcategories with categories Following good programming practices, can I create an Action SubcategoriesWithCategorie (without any domain) and through this Action access the Categories domain and the Subcategories domain?

odan commented 3 years ago

Hi @esallum-iluminare

The fundamental idea is to build the Application (and the API) around the higher-level use cases and not around the (lower-level) database tables. So please don't just build a "CRUD" API for each table. Build API for the concrete problems you are trying to solve. Focusing on capabilities is the key. I already mentioned this in my Slim 4 Tutorial

The most detailed answer to this question and other best practices can be found in my Slim 4 eBook. Chapter: Clean Architecture and ADR

You find good information in the documentation of this Slim skeleton project. There is also a video on Youtube that explains the concept very well: AVOID Entity Services by Focusing on Capabilities

esallum-iluminare commented 3 years ago

Thanks for the clarification. I will develop thinking about the routes and not the database CRUD.

In my specific case, my SubcategoriesWithCategorie action doesn't have any domain, because it uses the Categories domain and the Subcategories domain to return.

In my project it is necessary for me to have a route that returns the Categories, another that returns the Subcategories and another that returns both together. In this case, then my question is: due to good programming practices, in this case, an Action may not have a specific domain, and access two different domains?

odan commented 3 years ago

I don't know your exact requirements, so I try to answer it more general.

In general, I would try to split all these different use cases into specific, services. So for each endpoint (URL, route) there should be only one use case specific service class. If you have a piece of common logic, split this logic into a separate class and share it with the other classes. Take a look at the context and find out what this specific class does (SRP), then it should be clear how to name it. If you have a shared logic, try to find this shared logic and extract it into another class.

When you focus on the capabilities, you see that every use case is different.

Example:

So when you build an endpoint that needs data from multiple sources (tables) you also build a "use case specific query", that selects only the tables, fields you really need. The relation between the tables can be defined using a JOIN statement.

I have already answered similar questions here. I hope it helps. The eBook contains more details.

esallum-iluminare commented 3 years ago

Thanks for the answer.

In my case, my domain in question is as follows: https://pastebin.com/De24gQ4v .

My routes: //Subcategories w/ Category $app->get('/subcategories-with-category/{id_subcategory}', \App\Action\SubCategoryCategory\SubCategoryCategoryReadAction::class); $app->options('/subcategories-with-category/{id_subcategory}', PreflightAction::class);

As I understand it, the way I did it is wrong. The correct way is to create a Domain (with its respective Repository and Data) that contains a Service titled SubCategoryCategoryReader

Detail: I'm planning to buy your book :)

odan commented 3 years ago

The correct way is to create a Domain (with its respective Repository and Data) that contains a Service titled SubCategoryCategoryReader

Yes, correct. Just invoke one Service (not more) that handles the task (business logic) with the corresponding repository(s) that retrieves only the required data for that specific use case.