|
|
Re: Best practices in a thin-controller application: msg#00174
php.zend.framework.mvc
|
Subject: |
Re: Best practices in a thin-controller application |
Hi Marian,
Certainly, PoEAA has been an extremely helpful resource for me, but
beyond that, most of what I've gathered has been scattered across blogs
and articles over time. There are a few books, such as Data Modeling
Patterns by David Kay and Domain-Driven Design by Eric Evans, that are
on my to-buy list, but I haven't bought them yet. :)
Regards,
Bryce Lohr
Marian Meres wrote:
Hey Guys,
as it's probably the most helpful conversation I've ever had about
domain logic.
I absolutely agree and I just don't want this thread to end...
Besides already mentioned Martin Fowler's book (just bought it), what
books/resources would you suggest are worth reading on this subject?
Ideally something more conceptual/philosophical (rather than too
concrete, with some examples though), web application oriented if
possible... (I have not "googled" anything yet.)
Thanks again for inspiration.
Regards,
Marian
On 28.7.2008 18:54, Jonathan Lebensold wrote:
Hi Adam,
I've found that the Repository Pattern (PoEAA) has done me a lot of
good in medium-scale projects. When you might have a complex
underlying data model (with 1-m m-m and 1-1), it doesn't make sense
to expose multiple aggregate roots (ways of persisting) since it
complicates the interaction between the controller, which is
interesting in client interaction, and the persistence of data.
I start out by identifying aggregate roots and create repositories
around them. These repositories serve up data transfer objects (which
can be Doctrine or Zend_Db data objects) and then persist them.
The big advantages to this approach are thin controllers and
transactionality since repositories, by definition have a unit of
work associated with them. For me, its the Repository that provides
the "coarse api" that Bryce was referring to. If you're clever, you
can store multiple data transfer object types in one repository's
Unit of Work, thereby doing commits atomically.
Also, Zend_Form helps keep things thin. On my own projects, I've
managed to keep a small controller for complex forms by doing
validation in Zend_Form and flushing out the persistData() method
(which in turn fires against the Repositories in question). So with
the example you gave, I would've relegated that to a
Zend_Form-inherited object's persistData() method.
This way, the repositories, as the project grows, can be refactored
into classes that live inside inside the domain, which can be tested
and that the controller doesn't need to deal with.
my 2 cents,
Jon Lebensold
http://jon.lebensold.ca
On 28-Jul-08, at 11:58 AM, Bryce Lohr wrote:
Hi Adam,
Adam Jensen wrote:
1. A good domain model is unaware of its own underlying data source
(or even that it has one), and therefore cannot really be responsible
for its own persistence.
2. Code using the domain model (e.g., a controller action) shouldn't
really be aware of the technical details of persistence either.
This is where the philosophy part comes in to play: your rule #1
precludes perfectly valid patterns, such as Active Record, that
"know" something about the data source structure. You have to decide
based on your own philosophy, the app's requirements, and the
trade-offs of different data source strategies, which approach is
really the most useful.
[code]
public function someAction()
{
// The Mapper here is the only object that knows
// anything about the underlying data source.
$mapper = new Travel_DataMapper();
// Use the mapper to retrieve an existing domain object
$model = $mapper->findTrip(14);
// Change some arbitrary data
$model->traveler = 'Adam';
$model->departure = '2008-09-01';
$model->getDestinationByOffset(2)->country = 'MX';
// Pass the modified domain object back to the mapper for
persistence
$mapper->save($model);
}
[/code]
This seems ALMOST correct to me, but I'm still not sure I'm entirely
satisfied. The thing is, the controller is now aware of the
persistence layer, which seems to violate rule #2 above.
To me, this doesn't violate rule #2 (based on my very superficial
knowledge of what you're trying to achieve). Here, the data mapper
has a fairly abstract interface that doesn't appear to tie you to
any specific data source structure other than one identifying Trip
objects by an integer. This would probably be flexible enough for
most of the examples I've seen so far in this thread.
Then again, although the controller is aware of the persistence layer,
it doesn't know anything about the details of the persistence process.
So maybe it's not the worst thing in the world...after all, it seems
like persistence in a web application is mainly necessary due to the
stateless nature of HTTP transactions, and HTTP transactions are the
bread and butter of the controller layer.
I agree: in MVC web applications, I think the controller's main job
is to translate HTTP requests into some form of request upon the
application's domain model, and then to express the domain model's
results as something meaningful in HTTP. This means managing the
interaction with domain model objects, and persistence is still part
of the model layer.
That said, if you want, you can use something like a Service Layer
[1] to cleanly separate the HTTP-specific code from the
domain-object management code. Use the Service Layer as a facade to
create a very coarse API that would roughly correspond to what each
controller action needs to do with the domain model. You'd have to
decide if this would really be worth the effort for your application.
Anyway...I really appreciate the continued discussion here...we almost
ought to turn this into a tutorial, as it's probably the most helpful
conversation I've ever had about domain logic. Even the philosophical
parts...turns out I actually have a philosophy degree, so that's right
up my alley :)
Thanks!
Adam
Hope this helps,
Bryce Lohr
[1] http://martinfowler.com/eaaCatalog/serviceLayer.html
August 1st is National Minority Organ Donor Awareness Day
August 1st is National Minority Organ Donor Awareness Day
|
|