Now I am testing and refactoring the code in this regard, there are several questions on the correct application architecture in symfony.
The task of the controller is clear, you need to take the parameters, transfer it to the service, process the logic there and return the result to the controller. But often in the logic of services, you need to work out the data from the database. Therefore, there is a question, is it possible to call a database in services? I have almost every service or use dependency on the entity manager or certain repositories, is this normal? Or is it worth doing all this in the controller? Is it possible to save data to the database in the service? I have a service for payment, there I do all the checks, and then save the payment to the database. Of course, all this can be done through the controller and even easier (no need to introduce unnecessary dependencies), but without the if, else constructions can not be done.
The same question can be attributed to event listener and handler. I have a check, every time, how many days the user has paid. To do this, I appeal to the DB several times in the observer.
Am I doing the right thing? Or is it worth strictly limiting the functions of the service and controller? I will be glad to any tips.
- the controller should not have direct access to the database, only to the domain model representative. Services need access to the database. - etki
2 answers
You do the right thing keeping your controllers thin. The controller is the HTTP level, its possible tasks are the parsing of the request parameters (if they have not yet been parsed at the routing stage) and the design of the response (in the sense of the data format, HTML, JSON, etc).
Therefore, there is a question, is it possible to call a database in services? I have almost every service or use dependency on the entity manager or certain repositories, is this normal?
In general, yes, it is quite normal. But it is impossible to give a definite answer: it strongly depends on the specifics of your application, on the subject area, on the overall architecture.
Or is it worth strictly limiting the functions of the service and controller?
The service functions should be limited to its tasks (your KO). Remember single responsibility, do not create god-objects. Separate services into layers, bring general functions into separate libraries / components.
Good symfony logic separation practices are good software development practices in general, and such recommendations can only be as general as possible. For example, you can follow the DDD patterns. You can read Fowler . There are "official" Symfony Best Practices . I can also advise material from Doctrine's maintainer on good practices in ORM .
Read, choose what suits you best.
- Marco "ocramius" Pivetta recently had a report on this youtu.be/WW2qPKukoZY But there are a lot of controversial points - it’s good for warming up the brain, but you can't take everything for use in practice. - luchaninov
- oneYes, I am just on the slides from this report and left a link in the answer. IMHO, there you can (and often need to) use all of the above, but, of course, not mindlessly) - Timurib
If the project is not too complicated, then it is most convenient to make a single point for receiving entities from the DB - repository.
What the repository can do:
- findBySomething - get many entities by condition
- findOneBySomething - get one entity or null by condition
- findOneOrCreateBySomething - get one entity by condition, if not, create
- getBySomething - get one entity by condition or throw an exception if not
- countSomething - get the result immediately (sum, avg, count, max, ...) when it’s better than getting entities and processing them
- updateSomething - update data in the database, if it is better than getting entities, processing and doing
$em->flush
The repository should not contain complex logic that is not directly connected to the database. This is made in the services.
A service in dependencies can have a repository and, if necessary, an EntityManager.
The controllers communicate with the repository and EntityManager, if you just need to get the data from the form and save. If you need more complex logic, then through services.
This approach is suitable for most typical projects.
- oneBasically I agree with the recommendations, but I am ready to argue about the logic in the repositories. It all depends on how you use ORM: in the case of a rich domain model, the logic can be (and should be) even in entities. And the repositories are essentially the same services, they may well have domain logic (and usually it is there - at least at the level of DQL queries). - Timurib
- oneWhen there is less logic in the repositories, then: 1) more logic can be tested without a database; 2) it is easier to change the way data is received and saved (there is usually no point in changing the database, but it’s quite possible to change the data retrieval for a cache or external service) - luchaninov
- oneThe arguments are correct, but for each solution there is a case in which it does not fit. For example, 1) —the placement of logic in a DQL query can increase performance where it is more important for method testability, and 2) —the method of obtaining and storing data may also never change. The context is always different, especially when it comes to business logic. In general, I agree with you, I just wanted to draw attention to the fact that at the domain level, where the doctrine usually takes place, there can be anything. - Timurib
- oneBy the way, a little off topic, but maybe it will be interesting: a bit of criticism of the repositories in Doctrine and alternative solutions from beberlei - Timurib