As a follow up to my previous post I'm going to post some live examples of model layer with Zend framework based on Domain Driven Design concepts. I've spent the past few days studying up on domain driven design as well as a lot of Martin Fowler's work in relation to domain modeling and I think I have at least some code to start with that could get some conversations rolling. The issue is Zend Framework doesn't have a formal model layer and it shouldn't. Models are very much domain specific to what you're working on. As I've mentioned in my previous post sometimes you need complex business logic that will need to rapidly change and adapt to growing business needs. Active record can only get you so far when complexity expands.
I've posted a working Zend Framework project that has a basic working set of models so far. The current concept has a User who in turn owns a profile object. That profile object holds a nickname attribute that we can play around with. I have two tables users and profiles that are joined with a foreign key (user_id). Normally when you want profile information you'd just do a join and work with the result. What if that profile becomes context sensitive? What if a user can have a different profile based on the location he's in? Maybe he's a CIA agent and if he's overseas he needs a different profile to be covert. You'd now have to start adding conditions to your logic to account for this. Having the user own a profile object that can be injected at runtime allows for these contexts to be more manageable.
What if your user is going to be used in a workflow engine, a piece of marketing business logic that operates on users, it will be nice to be able to pass around a complete User object that has well documented properties and APIs instead of forcing each business logic service to know the details of your database.
Here is the google code repository you can checkout and play around with: [url=http://code.google.com/p/plushcode/source/browse/#svn/trunk/ZendModel]Google Code Repository for Zend Model project [/url]
It's all working, just create a database and dump in the zendmodel_DB.sql file, change your config.ini file settings and you're all set assuming you handle your apache settings
Take a look at the default/controllers/IndexController.php file for how the client code will be used.
// instantiate the user repository who will be in charge of building a proper user
$userRepo = new user_UserRepository();
// find a user with id 1 in the DB and grab their current nickname from the profile
$user = $userRepo->find(1);
$this->view->nickname = $user->Profile->getNickname();
// set their nickname in their profile to something new
// find the user again, it should pull the same object reference and not create a brand new object (Identity Map)
$user = $userRepo->find(1);
$this->view->nickname2 = $user->Profile->getNickname();
// a fictional web service to check to see how popular your new nickname is
$profileService = new services_ProfileService();
$popularity = $profileService->howCommonIsMyNickname($user);
$this->view->popularity = $popularity;
// let's update the user's email address
// you can manually commit the save to the database like this or in the destruct method of ObjectManager
// it will do it automatically for you, this will save all the changes we've made to all our objects
// using the unit of work pattern in ObjectManager, the profile and the user will get updated
We have a User model and we have some supporting files around it.
in models/user/ you'll see some files:
Each one plays a role within the domain User.php is the model itself that models a user in our system. It contains no SQL or knowledge of the persistence layer and can be passed around to any app on ths system. UserMapper.php is our basic DB Mapper that is in charge of doing the dirty work of putting things into our database. UserRepository is the default User Factory for creating a valid User object along with it's dependencies. Because this could be context sensitive we can contain that logic in a central place in there or add another Repository.
if you look in models/infrastructure/ObjectManager.php you'll see this file is a combination identity map and unit of work. It's job is to make sure if we have already instantiated a User object for example that we get back the same exact one and don't try to recreate it. It's basically an object cache.
Anyway, take a look and let's talk about what's right and what's wrong and how we can improve the complex domain model with Zend Framework. I'd like to start building on this example with everyday issues like logging, authentication, transactions, validation, unit tests, etc...