Rethinking the Zend Models
July 14, 2009
I'm currently in requirements mode for an upcoming project that should prove to be pretty complex. The current active record/table gateway patterns just aren't going to cut it for the complex business logic that's approaching. I'm starting to lean towards the domain model approach which would increase the initial complexity of the design but allow for the flexibility for future changes and features. The issue is with where to put your business logic? Most of the rails and Zend framework examples show a more active record type pattern which means that the business logic is tied into the persistence storage layer.
For example...
$user = new User();
$user->salary = 100;
$user->updatePayroll();
$user->save();
This works well for smaller apps where your models are tied tightly to your database structure however in my case I'll need to add the application layer above an existing database structure that may have 5 tables per object. For the example above I've diagrammed a possible solution

In this example I've created a User and Company class. Each one holds it's own data and basic operations. There is no sql in these, no direct knowledge of the outside world. Each model has a corresponding Mapper class (data mapper pattern) that knows how to save, find, create and delete it's own model. On top of those layers will sit a service layer that handles the real business logic. The UserPayrollService deals specifically with managing payroll for a user. UserAuthService is strictly for authenticating and logging out a user. As more and more complexity is added such as the need to have a User as part of a workflow engine I can simply create a new service or modify an existing one to implement the new business logic. Some additional benefits are that you can show the diagrams to your stakeholders and they can see concrete evidence that you've captured their business requirements as well as having the components testable on an individual level.
here are some examples of how it could be used...
Anyone have any thoughts on this implementation? What has served you well?
For example...
$user = new User();
$user->salary = 100;
$user->updatePayroll();
$user->save();
This works well for smaller apps where your models are tied tightly to your database structure however in my case I'll need to add the application layer above an existing database structure that may have 5 tables per object. For the example above I've diagrammed a possible solution

In this example I've created a User and Company class. Each one holds it's own data and basic operations. There is no sql in these, no direct knowledge of the outside world. Each model has a corresponding Mapper class (data mapper pattern) that knows how to save, find, create and delete it's own model. On top of those layers will sit a service layer that handles the real business logic. The UserPayrollService deals specifically with managing payroll for a user. UserAuthService is strictly for authenticating and logging out a user. As more and more complexity is added such as the need to have a User as part of a workflow engine I can simply create a new service or modify an existing one to implement the new business logic. Some additional benefits are that you can show the diagrams to your stakeholders and they can see concrete evidence that you've captured their business requirements as well as having the components testable on an individual level.
here are some examples of how it could be used...
/** * high level use case for an immediate login and update of salary */ $userAuth = new UserAuthService(); if($user = $userAuth->authorize($_request['email'], $_request['password'])) { $payroll = new UserPayrollService($user); $payroll->updateSalary(100000); $currentPay = $user->getSalary(); // currentPay would now equal 100000 } else { // auth fail }
/** * example of creating a new user */ $userMapper = new UserMapper(); if(!$userMapper->userExists($_request['email'])) { $user = new User(); $user->email = $_request['email']; $user->firstName = $_request['firstName'] $user->lastName = $_request['lastName'] $userMapper->save($user); redirect('/home'); } else { // user already exists }
Anyone have any thoughts on this implementation? What has served you well?
Thomas M says:
July 14, 2009 @ 15:15 — Reply
Moving towards splitting Objects into the "pure" classes and mapper objects seems to be the way that the Zend Framework pushes you (see the ZF Getting Started guides here: http://framework.zend.com/docs/quickstart/create-a-model-and-database-table). I do like what you've done above with the separate services layer which adds an additional layer but does a nice job of keeping business rules out of the mapping objects.
John Kleijn says:
July 15, 2009 @ 11:47 — Reply
I've been using a Service Layer for an application I am developing as well. I tend to be a little stricter about the boundary though: the Service Layer really is the boundary to the domain model, as diagrammed in PoEAA. I used to create Data Mappers as well, and while IMO they are far superior to Active Record design wise, lately I am using Doctrine because creating mappers for Domain Objects was just getting too time consuming. If there's ever a proper Data Mapper framework for PHP I am sure as hell gonna be in the front of the line though. A note though: AFAIK PHP didn't start supporting multiple inheritance... (your diagram should show aggregation -- or association if you want to be obscure)
Cory Kaufman says:
July 15, 2009 @ 12:58 — Reply
"I'm starting to lean towards the domain model approach which would increase the initial complexity of the design but allow for the flexibility for future changes and features. The issue is with where to put your business logic?" Uh... you put your business logic IN your domain objects. If your domain model doesn't contain your business logic, it's not a domain model...
Jim Plush says:
July 15, 2009 @ 13:31 — Reply
hi Cory, not sure you understood what I was saying. Of course your business logic is in your domain model however your domain model is not a domain object... a domain model contains things like entities, value objects, repositories, services, etc. Business logic may sometimes be context sensitive. For example a user model. If you try to put a user model in a club it may have certain validation rules compared with trying to put a user into a playground. So in this case the business logic is very much dependent on the context in which it's being used. So you'd most likely want to have your model free of this and have it injected in with a rules object that would be created by the context.
Andrea Turso says:
July 15, 2009 @ 17:25 — Reply
Currently there's a open proposal for implementing a component for Domain-Driven Design patterns in the Zend Framework. It seems they're building a configurable Data Mapper. The proposal is available at: http://framework.zend.com/wiki/display/ZFPROP/Zend_Db_Mapper+-+Benjamin+Eberlei I'm not working on the proposal but any help would be appreciated =)
Frank Desmettre says:
July 16, 2009 @ 09:23 — Reply
I'm actually using +- this concept of working with the Service layer and Domain model, but instead of the Mapper pattern to make queries, I'm using Db Table classes.
Jose M. Rodriguez says:
July 16, 2009 @ 21:54 — Reply
I see two different issues here...One, is domain objects distributed across several tables.The reason for this can be a few, including DB performance.Another, is decoupling bussiness logic from the model classes.Even more:decoupling object definition/object storage/object bussiness logic. While i think your second example is coherent with this view, the first one is a bit confusing to me.I cant see a good reason why getSalary is a method on "User", but updateSalary is a method on the Service. Personally, i prefer having the bussiness logic in the domain objects.I've seen the comment about different "contexts", but still, i'd handle those contexts using derived classes.I think services are more exposed to different contexts than model classes.The first problem (objects distributed in several tables) is more interesting for me, as it's related to my current "experiment".ActiveRecord-like implementations are really limited because they take information from the database to define models..My current experiment takes model information (definition) from files, where (and your blog post gave me the idea! Thanks!) i can define "aliases" for a field, indicating it'd be retrieved/stored in a different table, or even auto-map fields from other tables over the "main" model (the one who owns the primary key), if there's no field name clashing.
Jim Plush says:
July 17, 2009 @ 05:47 — Reply
hi Jose, yes I think you're right about the confusing aspect of having a getSalary on user and an updateSalary as a service. perhaps it would be more clear to inject the service object into the updateSalary method of a User instead to be able to change services at runtime. something like... //USER MODEL function updateSalary($service) { // let's assume this method has to do complex calculations when increasing salary such as what state // the user is in, what their tax rate is, etc... $newSalary = $service->increaseSalaryByPercent(5); $this->salary = $newSalary; }
Jose M. Rodriguez says:
July 17, 2009 @ 07:05 — Reply
I think that means three things we'd not really like.First, it makes the models aware of the service implementation.In that case, i'd prefer the "increaseSalaryBy..." to be a method on User, so User is calling itself, instead knowing a different object (the service).Second, the parameter $service for updateSalary means it has been created outside, to be used by "User".Dont you think User should create by himself the service object (as mostly all methods would require one)?It'd be something like a ServiceFactory, that you can change on the fly if you're interested in changing services at run-time.And third, i guess in most cases, $service would need a reference to the object model it's working on.I mean, increaseSalaryByPercent will need to know the current user salary,so it'll need a reference to it (not simply a "salary" parameter, i think).So User needs the $service which in turn needs the User...I try to avoid this kind of patterns. Btw, it's great that you mention "state", as i think it's one of the main issues to resolve this kind of problems, and i think it's something the frameworks should handle for us.The other important thing are permissions!
Jose M. Rodriguez says:
July 17, 2009 @ 07:09 — Reply
Oh, sorry, just a final thought! Dont you think Service is getting near a Proxy/Façade class for User? Oh, and sorry if i sound negative!It's not like that at all! I'm really enjoying this issue, i'm very interested in it too! :-)
Jim Plush says:
July 17, 2009 @ 07:44 — Reply
hi Jose, I think it comes down to personal preference at that point because you have to create these dependencies somehow and there are only a handful of ways to do it, constructor injection, method parameter injection, setters, service locators, etc... If updating a user's payroll relies on information stored in the persistence layer then it has to query the DB at some point. If updateSalary is a method on the user the user still needs to rely on something outside of itself to get the info it needs to increase it. Which would leave you a few choices Have the updateSalary method call a service to get the salary amount, have the updateSalary method do the db querying itself(not recommended)... I'm still searching for the answer myself as to where this logic should really belong. Ideally User should be as decoupled as possible from the outside domain but to do it's business logic it needs outside information sometimes. I suppose you could remove the burden from the User all together and just have the client use a PayrollService and the User only holds a currentSalary property which would decouple it which is shown in the original example.
Jim Plush says:
July 17, 2009 @ 11:13 — Reply
hi Cory, I think my concerns about the $user->updateSalary() method is that if you have a business rule that says "An admin cannot update a user's salary higher than their home state's average cost of living increase, and not 20% higher than any other employee in their department" So the information needed is the salaries of all others in their department (let's assume that's in the users table) but we also need the state's annual cost of living percentage. Let's assume that's in a seperate table or even a web service we have to call. If I pass a userMapper object to the $user->updateSalary($amount, userMapper); the user mapper would have knowledge of not only the users table but it would have to know how to find that other source of information on the state's living increase percentage. That doesn't seem right to me. I've been trying to find out people handle these situations.
London says:
July 21, 2009 @ 03:06 — Reply
A function is missing on the diagram. $userAuth->authorize, there is no such function in UserAuthService, UserMapper or User. Where would you put it? UserAuthService? Great Article!
christian louboutin says:
January 6, 2010 @ 11:47 — Reply
Comment pending moderation
Louis Vuitton Handbags Replica says:
January 29, 2010 @ 18:11 — Reply
Comment pending moderation
gucci handbags replica says:
January 29, 2010 @ 18:12 — Reply
Comment pending moderation
Louis Vuitton Handbags Replica says:
January 29, 2010 @ 19:17 — Reply
Comment pending moderation
replica cartier watches says:
March 9, 2010 @ 00:01 — Reply
Comment pending moderation
fake breitling watches says:
March 9, 2010 @ 00:03 — Reply
Comment pending moderation
Renewal By Andersen says:
March 11, 2010 @ 01:35 — Reply
Comment pending moderation
GED Online says:
April 3, 2010 @ 00:29 — Reply
Comment pending moderation
Online High School Diploma says:
April 3, 2010 @ 00:30 — Reply
Comment pending moderation
Homeschool diploma says:
April 3, 2010 @ 00:31 — Reply
Comment pending moderation
Get high school diploma says:
April 3, 2010 @ 00:32 — Reply
Comment pending moderation
Nation High School says:
April 3, 2010 @ 00:34 — Reply
Comment pending moderation
online colleges and universities says:
April 10, 2010 @ 06:17 — Reply
Comment pending moderation
SharePoint Courses says:
April 10, 2010 @ 11:04 — Reply
Comment pending moderation
acne remedies says:
April 15, 2010 @ 10:29 — Reply
Comment pending moderation
one day diet says:
April 15, 2010 @ 21:55 — Reply
Comment pending moderation
blu ray ripper says:
April 18, 2010 @ 04:32 — Reply
Comment pending moderation
organic mattresses says:
April 22, 2010 @ 00:49 — Reply
Comment pending moderation
Business Lawyer Colorado says:
April 27, 2010 @ 11:36 — Reply
Comment pending moderation
Muscle Building says:
April 29, 2010 @ 06:20 — Reply
Comment pending moderation
graphic design agency london says:
April 29, 2010 @ 13:04 — Reply
Comment pending moderation
Puzzles & Word Games says:
April 30, 2010 @ 02:19 — Reply
Comment pending moderation
youtube mp3 converter says:
April 30, 2010 @ 05:33 — Reply
Comment pending moderation
learn futures trading says:
May 3, 2010 @ 09:27 — Reply
Comment pending moderation
male enchancement says:
May 3, 2010 @ 09:31 — Reply
Comment pending moderation
make money online says:
May 3, 2010 @ 22:42 — Reply
Comment pending moderation
Muscle building program says:
May 4, 2010 @ 02:25 — Reply
Comment pending moderation
chaga says:
May 4, 2010 @ 03:03 — Reply
Comment pending moderation
Satellite Internet says:
May 6, 2010 @ 19:26 — Reply
Comment pending moderation
ev dekorasyonu says:
May 10, 2010 @ 04:24 — Reply
Comment pending moderation
Replica Louis Vuitton Speedy says:
May 10, 2010 @ 18:03 — Reply
Comment pending moderation
Manolo blahnik says:
May 14, 2010 @ 04:59 — Reply
Comment pending moderation
mobilya modelleri says:
May 18, 2010 @ 14:48 — Reply
Comment pending moderation
cilt bakimi says:
May 18, 2010 @ 14:52 — Reply
Comment pending moderation
642-067 says:
May 19, 2010 @ 00:19 — Reply
Comment pending moderation
642-062 says:
May 19, 2010 @ 00:20 — Reply
Comment pending moderation
law and order season 20 episode 22 says:
May 21, 2010 @ 04:54 — Reply
Comment pending moderation
one tree hill season 7 episode 22 says:
May 21, 2010 @ 04:55 — Reply
Comment pending moderation
Clasamente says:
May 21, 2010 @ 11:20 — Reply
Comment pending moderation
HID lights says:
May 23, 2010 @ 03:30 — Reply
Comment pending moderation
up2drive reviews says:
May 25, 2010 @ 04:12 — Reply
Comment pending moderation
seo secrets says:
May 27, 2010 @ 04:07 — Reply
Comment pending moderation
Christian Louboutin says:
May 28, 2010 @ 06:23 — Reply
Comment pending moderation
Abercrombie And Fitch says:
May 28, 2010 @ 06:25 — Reply
Comment pending moderation
ugg boots Sale says:
May 28, 2010 @ 06:26 — Reply
Comment pending moderation
ghd says:
May 28, 2010 @ 06:28 — Reply
Comment pending moderation
vibram five fingers says:
May 28, 2010 @ 06:29 — Reply
Comment pending moderation
Tall ugg shoes says:
May 28, 2010 @ 19:30 — Reply
Comment pending moderation
Ugg Boots UK says:
May 28, 2010 @ 19:32 — Reply
Comment pending moderation
uggs shoes says:
May 28, 2010 @ 19:36 — Reply
Comment pending moderation
ugg boots says:
May 28, 2010 @ 19:39 — Reply
Comment pending moderation
uggs boots says:
May 28, 2010 @ 19:41 — Reply
Comment pending moderation
vibram shoes says:
May 28, 2010 @ 19:43 — Reply
Comment pending moderation
Cheap EdHardy says:
May 28, 2010 @ 19:45 — Reply
Comment pending moderation
hollister says:
May 28, 2010 @ 19:48 — Reply
Comment pending moderation
Jackets says:
May 28, 2010 @ 21:57 — Reply
Comment pending moderation
Ugg Classic Cardy says:
May 28, 2010 @ 22:00 — Reply
Comment pending moderation
Christian Louboutin Boots says:
May 28, 2010 @ 22:03 — Reply
Comment pending moderation
Ben10 games says:
May 29, 2010 @ 23:00 — Reply
Comment pending moderation
Adam says:
May 30, 2010 @ 12:39 — Reply
Comment pending moderation
memory foam mattress says:
May 31, 2010 @ 20:58 — Reply
Comment pending moderation
Revitol Stretch Mark says:
June 1, 2010 @ 03:50 — Reply
Comment pending moderation
baby prams says:
June 1, 2010 @ 23:04 — Reply
Comment pending moderation
kill lice says:
June 2, 2010 @ 20:42 — Reply
Comment pending moderation
Crossbow says:
June 2, 2010 @ 22:26 — Reply
Comment pending moderation
asadfahim says:
June 4, 2010 @ 01:01 — Reply
Comment pending moderation
Dak jai says:
June 4, 2010 @ 02:05 — Reply
Comment pending moderation
virbram five fingers says:
June 4, 2010 @ 21:03 — Reply
Comment pending moderation
Free Games Online says:
June 17, 2010 @ 09:50 — Reply
Comment pending moderation
asadfahim says:
June 5, 2010 @ 12:32 — Reply
Comment pending moderation
70-528 says:
June 6, 2010 @ 23:04 — Reply
Comment pending moderation
Stromlieferant says:
June 7, 2010 @ 05:17 — Reply
Comment pending moderation
poker bonus says:
June 8, 2010 @ 05:29 — Reply
Comment pending moderation
Outlet Pillow says:
June 9, 2010 @ 08:01 — Reply
Comment pending moderation
Video Luna Maya says:
June 10, 2010 @ 06:14 — Reply
Comment pending moderation
Farm Games says:
June 10, 2010 @ 09:54 — Reply
Comment pending moderation
2fanonline says:
June 10, 2010 @ 23:42 — Reply
Comment pending moderation
kelowna bc says:
June 11, 2010 @ 04:07 — Reply
Comment pending moderation
ny limousine says:
June 11, 2010 @ 04:09 — Reply
Comment pending moderation
calgary online marketing says:
June 11, 2010 @ 04:12 — Reply
Comment pending moderation
rent edmonton says:
June 11, 2010 @ 04:14 — Reply
Comment pending moderation
stress at work says:
June 11, 2010 @ 04:15 — Reply
Comment pending moderation
YourNetBiz says:
June 11, 2010 @ 09:26 — Reply
Comment pending moderation
YourNetBiz says:
June 11, 2010 @ 09:29 — Reply
Comment pending moderation
London Limousine says:
June 11, 2010 @ 09:31 — Reply
Comment pending moderation
acuvue oasys says:
June 11, 2010 @ 09:35 — Reply
Comment pending moderation
air jordan shoes says:
June 11, 2010 @ 19:58 — Reply
Comment pending moderation
nike air force 1 says:
June 11, 2010 @ 19:59 — Reply
Comment pending moderation
cell phone battery says:
June 12, 2010 @ 00:54 — Reply
Comment pending moderation
Dubai Web Application Development says:
June 12, 2010 @ 04:10 — Reply
Comment pending moderation
Water Proof Jackets says:
June 13, 2010 @ 08:17 — Reply
Comment pending moderation
SEO says:
June 14, 2010 @ 04:25 — Reply
Comment pending moderation
Search Engine Optimization says:
June 14, 2010 @ 05:12 — Reply
Comment pending moderation
Custom Sticker printing says:
June 15, 2010 @ 00:12 — Reply
Comment pending moderation
Pregnancy Miracle says:
June 15, 2010 @ 04:35 — Reply
Comment pending moderation
Indonesia Furniture Handicraft Wholesale Marketplace says:
June 15, 2010 @ 10:21 — Reply
Comment pending moderation
Louis Vuitton handbags says:
June 16, 2010 @ 00:20 — Reply
Comment pending moderation
Louis Vuitton handbags says:
June 16, 2010 @ 00:30 — Reply
Comment pending moderation
iPhone app says:
June 16, 2010 @ 06:35 — Reply
Comment pending moderation
christian louboutin shoes says:
June 16, 2010 @ 08:33 — Reply
Comment pending moderation
food pyramid says:
June 16, 2010 @ 22:22 — Reply
Comment pending moderation
online traffic school Los Angeles says:
June 16, 2010 @ 22:26 — Reply
Comment pending moderation
cheap insurance says:
June 16, 2010 @ 22:29 — Reply
Comment pending moderation
ski goggles says:
June 16, 2010 @ 22:31 — Reply
Comment pending moderation
Jillian Michaels diet pills says:
June 16, 2010 @ 22:33 — Reply
Comment pending moderation
Promotional Gifts says:
June 17, 2010 @ 05:23 — Reply
Comment pending moderation
Promotional Products says:
June 17, 2010 @ 05:41 — Reply
Comment pending moderation
Corporate Gifts says:
June 17, 2010 @ 05:49 — Reply
Comment pending moderation
seotrafficstorm.com says:
June 17, 2010 @ 23:53 — Reply
Comment pending moderation
Bvlgari jewelry says:
June 18, 2010 @ 01:21 — Reply
Comment pending moderation
hermes belts says:
June 18, 2010 @ 01:22 — Reply
Comment pending moderation
evening dresses says:
June 18, 2010 @ 08:33 — Reply
Comment pending moderation
Microsoft Office 2007 says:
June 19, 2010 @ 02:16 — Reply
Comment pending moderation
kobe bryant shoes says:
June 19, 2010 @ 05:20 — Reply
Comment pending moderation
kobe bryant shoes says:
June 19, 2010 @ 05:22 — Reply
Comment pending moderation
kobe bryant shoes says:
June 19, 2010 @ 05:23 — Reply
Comment pending moderation
fat burning furnace scam says:
June 19, 2010 @ 13:00 — Reply
Comment pending moderation
Luxury car rental Miami says:
June 20, 2010 @ 02:11 — Reply
Comment pending moderation
Forex Robot says:
June 20, 2010 @ 16:51 — Reply
Comment pending moderation
Mortgage Loans says:
June 20, 2010 @ 16:55 — Reply
Comment pending moderation
Weight Loss Diet says:
June 20, 2010 @ 16:56 — Reply
Comment pending moderation
Quality Top Site says:
June 20, 2010 @ 16:57 — Reply
Comment pending moderation
Weight Loss Tips says:
June 20, 2010 @ 16:59 — Reply
Comment pending moderation
Idahoagbell says:
June 20, 2010 @ 17:01 — Reply
Comment pending moderation
General Weblog says:
June 20, 2010 @ 17:02 — Reply
Comment pending moderation
Online Games says:
June 20, 2010 @ 17:03 — Reply
Comment pending moderation
Golden Casino Games says:
June 20, 2010 @ 17:05 — Reply
Comment pending moderation
Personal Blog says:
June 20, 2010 @ 17:06 — Reply
Comment pending moderation
Promo Codes says:
June 20, 2010 @ 17:08 — Reply
Comment pending moderation
Wallpapers Nintendo says:
June 20, 2010 @ 17:11 — Reply
Comment pending moderation
Diacetate says:
June 20, 2010 @ 17:12 — Reply
Comment pending moderation
Colonial Life Insurance says:
June 20, 2010 @ 17:15 — Reply
Comment pending moderation
Cheap Hotels says:
June 20, 2010 @ 17:18 — Reply
Comment pending moderation
free music download mp3 says:
June 20, 2010 @ 21:13 — Reply
Comment pending moderation
birthday message says:
June 20, 2010 @ 23:28 — Reply
Comment pending moderation
Replica Jewelry says:
June 21, 2010 @ 00:45 — Reply
Comment pending moderation
Desks says:
June 21, 2010 @ 06:25 — Reply
Comment pending moderation
Download Full Movies says:
June 21, 2010 @ 22:40 — Reply
Comment pending moderation
Sticker Printing says:
June 21, 2010 @ 22:43 — Reply
Comment pending moderation
Background Check says:
June 21, 2010 @ 22:45 — Reply
Comment pending moderation
watch free movies says:
June 21, 2010 @ 22:46 — Reply
Comment pending moderation
Rent Movie Online says:
June 21, 2010 @ 22:47 — Reply
Comment pending moderation
john says:
June 21, 2010 @ 23:55 — Reply
Comment pending moderation
tracy says:
June 21, 2010 @ 23:56 — Reply
Comment pending moderation
Phentermine 37.5 says:
June 22, 2010 @ 03:58 — Reply
Comment pending moderation
ceramic sink says:
June 25, 2010 @ 00:19 — Reply
Comment pending moderation
Andri says:
June 26, 2010 @ 05:12 — Reply
Comment pending moderation
32 lcd tv says:
June 26, 2010 @ 06:46 — Reply
Comment pending moderation
ceramic watches says:
June 26, 2010 @ 07:11 — Reply
Comment pending moderation
herpes in mouth says:
June 27, 2010 @ 04:39 — Reply
Comment pending moderation