Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Where to build new domain entities? Controller, repository, or mapper?

Let's say for each domain entity, I have a repository that provides an API to a data mapper. For example, if I have a UserEntity, then I would have a UserRepository that speaks to a UserMapper to persist user data in a database.

Now, let's say a form is submitted on a web page, and my controller knows it needs to create a new UserEntity based on the submitted info.

Does it:

  1. do new UserEntity() right there on the spot, and run all the necessary setter methods according to the form data submitted, then pass the UserEntity to the repo, who passes to the mapper for insertion?

    Controller creates UserEntity => Repo => Mapper => DB

  2. turn the form data into an array, and pass it to the UserRepository who then runs new UserEntity() and the setters, and passes it to the mapper for insertion?

    Controller passes user data => Repo creates UserEntity => Mapper => DB

  3. pass the array to the UserRepository, who passes the array to the mapper for new UserEntity and insertion?

    Controller passes user data => Repo passes user data => Mapper creates UserEntity => DB

Whose responsibility is it to manage the creation of objects?

like image 207
johnnietheblack Avatar asked Mar 15 '12 21:03

johnnietheblack


1 Answers

I know this question is old, but I thought I'd throw my thoughts in here since the only answer hasn't been officially accepted and this may be helpful to future searchers. To directly answer your question, I'd say none of the above. I prefer to have an extra service, a "manager" if you will, for brokering the interaction between the controller and the repo/mapper objects. Each model has a dedicated manager for handling its creation, updates, and deletion.

Controllers

I consider the controller as the glue of the application. We can separate concerns all we want into as many pieces as possible, but somewhere along the line, something has to understand both the view side and the model side, and that object is the controller. That being said, I believe controllers should be skinny, so the controller's only real job is to map a request to a response. Any kind of intermediate processing should be kicked off somewhere else.

In a CRUD app, it's pretty easy to get away with instantiating new objects and persisting them in the controller, even it is done more than once, because it is only a few lines to paste. What if object creation was not trivial? I'm maintaining an application with many complex relationships and a user-submitted create often entails the creation of many objects simultaneously. This is not feasible to maintain in a controller-and-model-only environment.

Extra Service Layers

To handle this, I've created 2 extra service layers: FormHandler and Manager. Every time a form is submitted, the form contents get sent to the form handler layer. Form handlers are responsible for understanding the form data coming in and normalizing it. The form handlers can then pass off the data to appropriate manager objects for processing. Manager objects process the data and update the domain layer. They are responsible for creating models, changing models, and persisting them to the backend.

In this way, the controllers have knowledge of Request, Response, Form (possibly, if your framework supports server-side form creation), and FormHandler. Form handlers have knowledge of Form (or form data), and Manager. Manager has knowledge of Repository, Mapper, and Model. Notice, now, Managers are the sole point of interaction with Models and the Mapper, and they have no knowledge of the Form data or the Request or Response. On the flip side, the controllers and form handlers don't need to know about domain layer data or persistence.

Conclusion

With this design:

Controller -> FormHandler -> ModelManager -> Mapper

I've found all my classes are now unit-testable (even controllers to some degree) due to separation of concerns being nicely split out, and the single point of interaction is a boon for avoiding duplicate logic.

Notes

The repo in my mind is only for querying the database -- asking it if it has something, not creating new things.

My experience in this case is from using Symfony 2 and Doctrine 2.

YMMV; e.g. you may find the form layer is unnecessary, but I've found it very handy for data transformation from form/view data into something the domain models understand.

like image 187
Nate Avatar answered Nov 15 '22 09:11

Nate