2008年12月11日星期四

Re: [fw-mvc] Pure Domain OO Modelling

Hi Aaron,

Am Mittwoch, den 10.12.2008, 20:15 -0800 schrieb netlynx:
[...]
> Now the application I am working on is by no means enterprise class material
> here, however being a developer, I have dreams of it being enterprise class
> material one day,

One of the most important principles is YAGNI[1]. If there is no reason
to do it, except for training, why do you want to do it? If your
application is simple, maybe the simplest solution (and therefore the
best) ist simply the best for your. But, the big "but", there is also
"thinking ahead would have helped"[2]. And now we have a problem: how to
find out, if you need it. I guess you already know the answer to this
question, most of the time you have an intention and most of the time
your intention is right.

[...]
> so my thought is to do it properly from the get go, *and*
> using pure OO design makes unit testing easier (I read, if anyone can point
> me to some good resources on the best way to do unit testing / learn more
> about it, I would be most appreciative).

As formerly quoted here, Domain Driven Design by Evans is a great
resource. Read it first and then Enterprise Application Patterns by
Fowler. If you don't got into that Design Patterns thing at all, read
the classic "Design Pattern" after Domain Driven Design (or parallel)
and then start with Enterprise Application Patterns. Except from
grasping a few basic concepts, the most important part is do it. Your
first domain logic will suck pretty hard (if you are lucky, you find
another job after finishing it or the company is sold or anything else
that leads to the result your don't have touch it again), but your
second try will suck less and so on.
The messy part of domain logic is, that you can't do it by yourself. So
if you decide "I want to do it properly" it doesn't matter if you go
alone and leave your team or your stake holder behind. So "I want to do
it" is a valid starting point but "we want to do it properly" must be
the result, otherwise you are doomed to fail.

> Anyhow, one other post I was reading something was mentioned about
> separating the data from the source. (hence not extending the
> Zend_Db_Table_Abstract class). I have created a class with magic methods
> for getting and setting properties (actually all in an array called _data),
> and the magic methods define what can and can not be set, filters the data
> being set, and takes care of what changes are pending (that need to be
> written).

I hope "separating the data from the source" was not propagated as a way
to implement domain logic. It might be important (that's what all DBALs
do) but more important is to separate persistency (your database, some
random service, some random XML file and so on) from your business logic
(the code that represents the mess of business, that's why I prefer to
call it business illogicality).
This also means you can use Zend_Db_Table and _Row happily, but it is
not your model, it is your persistency layer.

> Now I have two data sources to read from:
> First: I have a database table, where I check first if the record exists.
> Second: I have another database table (completely different custom database
> server/type, where I check second if the first returns non-existant (if the
> second one is successful, I grab the record, and write it to the first
> database (duplicating the record), and if the record doesn't exist I have it
> throw an exception stating that the record doesn't exist in either database
> table.)

class BusinessProcessFoo
{
public function __construct()
{
$this->_table = new MyDbTable1();
$this->_table = new MyDbTable2();
}

/**
* We assume the reason for duplicating lays in executing the
* process
*/
public function executeProcess()
{
/**
* I don't know Zend_Db_Table good enough to know how to
* duplicate records, so imagine the code here
*/
}
}

This is the most basic example. But it sucks. Better version might have
a factory which creates the business process object with the configured
database tables (dependency injection), it might use strategies how to
do the lookup in the tables and so on.
I want to point out one major flaw in your description of the issue: you
thought about data, not about actors and responsibilities. In OOP there
is no such thing as "data" (yes, there is, but I always deny this). This
is due to the reason, as data is maybe the most abstract type in
programming. In OOP we think about objects with a clearly defined
responsibility (and therefore a "role" in the system). The object might
contain "data", but that data is defined. So the implementation for your
problem above might be "try to read one user from db1, if not found, try
from db2, if found, make sure it is stored in both, if not found, throw
an exception". That's the design detail while the abstract story is
"create a user object". How this construction is done? I don't care, I
just want to use that object.
Just a hint: there is something like data-drive analysis/design and
sometimes this is a good idea. The important thing is to know about both
and to use which analysis tool to apply in the specific situation (trick
question: what method would you use if you would build Facebook and what
would you use if you build a shop system).

> Could someone give me a simple example of how to use the pure OO method to
> handle this scenario?
> I am assuming that I would pass an adapter (through the constructor) and
> keep the business logic somewhere other than the model, but I am not quite
> seeing how to do this properly. (and if you could show me a quick example of
> unit testing that would be great too).

Yap, the adapter thing goes into the right direction. When doing this
you can inject a mock object for the database tables and test it easily
(and without depending on the database).

[1] http://c2.com/xp/YouArentGonnaNeedIt.html
[2]
http://c2.com/cgi/wiki?DavesRealExampleWhereThinkingAheadWouldHaveHelped

cu, Lars, and sorry for the long post :)

没有评论: