Objects in the model layer
I currently use a very simple set of core objects within my model layer: entities, mappers and service objects.
Entities are objects that represent something in my business logic. For example, in my traditional Album’s tutorial, the entity would be the object that holds one album. It has properties such as title, artist and date created and methods that are specific to this entity.
Mappers know how to save and load an entity from the data store. This could be a database or a web service or an CSV file on disk. There is no requirement that a given entity maps to a single database table (or file on disk) as the mapper can simply use multiple tables for different properties within the entity if it wants to. The entity has no knowledge of how it is loaded and saved. This isolation means that I can have multiple mappers for the same entity that store it to different data stores.
Service objects provide the API that the rest of the application uses. I allow controllers and view helpers to talk to service objects, though I appreciate that others have a different take on MVC. Any given service object knows about mappers and entities and anything else that the business logic requires. I like having a service object as I can rework which mappers do what without having to touch the rest of the application. The service layer also know about other app details such as sending emails after a form is submitted. In an event based system, such as a ZF2, these details can now live in their own objects which listen for events triggered by the service object.
I dislike the phrase “service object” as the word “service” means so many things to so many people. I haven’t heard a better phrase yet that everyone understands though.
This is exactly how I organize my code. It works very well for me.
One question about mappers, especially now related for zf2. When a mapper uses the database for storage, do you extend some part of ZendDb, or do you inject what you need? I guess it's the latter, because that would be the easiest way to have one mapper work with multiple tables. And the same goes for web services.
What when you need to work with a custom storage, for example a CSV file – do you put the file read/write logic into the mapper, or do you create another object and then inject that to the mapper? Again, I guess it's the latter as it would make for easier testing.
Robert,
I tend to extend ZfcBaseAbstractDbMapper as you can override the default table name for all its methods when calling them.
For custom storage, it depends on the complexity :)
It's very similar to my approach.
How do you deal with relations. Do you use the 'Domain Service' to fetch the related entities?
For example:
$albumService->getArtist($album);
Is there a rule of thumb to decide which entity requires a service? until now, I tend to only create services for aggregated roots, otherwise I end up with anemic services (proxying every method to the related mapper).
Thanks
Use Doctrine and get rid of mappers
jaapverloop,
It depends. I've used a getArtist() method on the service object and I've also made loadAlbumById() also fetch and populate the artist information.
Hi,
i WOULD LIKE to organize my code like that, i still don't understand how use service, what i have to make in the Controller, and what i have to make in the service.. do you have an example or any document?
Thanks
Guilherme,
I'm intending to write some more practical articles about this over the next weeks.
That's perfect… thank you! :)
Identical to my approach, and terminology, inspired by Domain Driven Design.
I like the term "service" though, because it is generic – I don't like inventing additional terminology for different types of services, so in my mind, anything that "services" the overall application, is a "service".
@Guilherme:
In my applications, Controllers exclusively deal with web-related aspects of processing a request – anything not web-related (business logic) is implemented as services.
To use a concrete example, a Controller that changes a User's password is responsible for obtaining the User instance, invoking the password service to change the user's password, and rendering a view that confirms the change.
Actually hashing/salting/saving a password is considered business logic, so the password service is responsible for doing the actual work, invoking a logging service to record the event, etc.
By isolating this responsibility in a service, it becomes easier to reuse. For example, I can change the password of either a User or a Customer Entity via the same service API – and I can set a User's password either from a user registration or user profile Controller.
Thanks so much for writing about this! I've been a huge proponent of this architecture everywhere I go. I have been doing presentations on it as well – check this out: http://www.slideshare.net/aaronsaray/midwest-php-2013
And to the person who said "get rid of mappers, use doctrine" – In enterprise applications, sometimes you are not just dealing with one database – you might be using entirely different data sources entirely. Perhaps part of your entity (model) gets populated using database (so, by all means, you CAN abstract out another layer using Doctrine) – but another part of it requires Salesforce data… this is where that mapper really shines. It can also go out to salesforce, map that equivalent data. And at the end, it returns the fully populated entity model, agnostic of the underlying storage(s).
Aaron,
That's an excellent presentation.
Since the service method orchestrates the business logic and invokes other services (eg mappers), how does one determine which should be invoked directly or triggered via events. And in the case of events, where and when would these listeners be registered?
I've been using the term Manager for the Service object you describe. I also consider Mappers to be service objects and thus prefer more descriptive terminology.
What about making the Service object implement the Aggregate Listener Interface?
When the Event Manager for that controller is set, you can then add the service manager
$events->attach($serviceManager->get('ServiceObject'));
The attach method of the ServiceObject would then register listeners for all of its API methods
$this->listeners[] = $events->attach('member.create', array($this, 'createMember'));
This would allow the Service method to not have to worry about pre and post event notices per se. Or for example allow additional checks (e.g ACL) to be attached externally prior to executing the service method, and maybe even log events after the method has been executed.
The ZendMvcViewHttpViewManager is an example.
Something that has been bothering me for a while is the notion of the service object triggering events
Where are
I've been pondering this for a while now
Rob, how do you organize collection of entities? Is it some special EntityCollection object or simple array?
Just a quick question; when you say "mappers" would this be similar to say a repository interface that is then implemented for a specific data-store? Ergo, IFooRepository and then MySqlFooRepository (which takes entities as arguments where necessary)? The only reason I ask is that in the .net world I am used to a "mapper" in the sense of AutoMapper which is completely different from your description above.
Thanks,
Michael,
Maybe http://akrabat.com/php/objects-in-the-model-layer-part-2/ will help?
Rob, I just viewed that a moment ago. Yes, it would seem that what I do is similar to you. The only difference (if the example is literal) is that I define an interface and program against that.