[Rails] Program logic behind Ruby On Rails
Stefan Arentz
stefan.arentz at norad.org
Sat Jan 15 09:25:48 GMT 2005
On Jan 14, 2005, at 9:03 PM, Michael Koziarski wrote:
>> I agree that it is bad having the logic in controllers. But I also
>> don't like to put business logic in in the domain model. I try to keep
>> the model objects as lightweight as they can be; if I add logic to
>> them
>> it is usuall for handling relationships between model objects or
>> simple
>> getter/setter logic.
>>
>> My approach is this: I move all business logic, the glue that actually
>> puts model objects together to make sense as a whole, in a Service
>> Layer. Personally I think this is a much cleaner design because it
>> doesn't pollute the domain model with application logic.
>>
>> http://www.martinfowler.com/eaaCatalog/serviceLayer.html
>
> I can quote Fowler too ;). What you're describing is an anemic domain
> model, and the style of programming is quite common in java (IOC
> containers especially).
>
> http://www.martinfowler.com/bliki/AnemicDomainModel.html
>
> "In general, the more behavior you find in the services, the more
> likely you are to be robbing yourself of the benefits of a domain
> model. If all your logic is in services, you've robbed yourself
> blind."
In that article he calls himself an 'object bigot'. I guess I'm just a
bit more pragmatic than that. I understand what he is saying, but in
practice (been using these patterns for a long time) it doesn't matter
much. The end result is a nicely layered application where persistence,
business logic and gui logic are all seperate. And easily pluggable
(with different implementations or mock objects for example).
Also, I quoted that page for the picture, not for Fowler's highly
academic view on software development. Personally I think he has lost
touch with reality.
Anyway, my applications usually look like this:
Backend:
Domain Model Objects: User, PhoneNumber
These are plain objects. They have NO idea about persistence and do not
inherit from any persistence framework. That is handled by the
persistence engine transparently. It keeps them dumb. They simply use
the language features for relationship mapping to other objects;
arrays, maps and object references.
Why is this good? I might also want to store a domain object in memory
or use it in a GUI using a typical Command pattern. Then I don't want
to have: a) all the extra baggage that for example ActiveRecord's
persistence logic adds to the object and b) don't want to expose all
the business logic to the user of that object.
And yes, these are used as 'data records'. I only put logic in there
that works with the data in the object or its relations. Like
TodoList.add_todo Subscription.expired? or User.encode_password
Data Access Objects: UserManager, TodoManager, or simply a global
EntityManager
These objects contain all the 'base' operations on the domain model.
Things like createUser, disableUser, findUsersByCountryCode,
deleteUser. They do not contain application logic. They typically
interact with the persistence layer, wether it is contained in an
ActiveRecord style or an O/R mapper like TopLink.
These are all build on interfaces. UserManager is an interface,
UserManagerImpl contains the actual implementation.
Application Logic Objects: AddressBookLogic
(These are what I confusingly called the 'Service Layer' in my previous
email)
Just as the DAOs these are all coded against interfaces. This is great
because when I for example want to test if a mailer object works ok
then can I simply change the reference from MailerImpl to
MailerMockImpl which instead of actually sending email through SMTP
will simply log its actions to a file. Plug and play. Great for
testing. The IoC container takes care of this, there are NO references
to actual implementations in the code, just references to the
interfaces.
These objects contain all the 'strategies' of the application. Things
like signupUser (unit of work: createUser, createHomepage,
createProject, send an email) or createDefaultProject (unit of work:
createProject, createDefaultProjectCategories).
Service Objects:
(Not what Fowler describes)
Usually I need to expose Application Logic to an external system
through some kind of web service or remoting API. What I do then is
build a thin layer in what I call a Service object. These interact
directly with the Application Logic.
For example say we have a Todo application. In the Application Logic
domain i would create a TodoLogic object like this:
TodoLogic.createTodo(user, todo_name, todo_priority)
TodoLogic.listTodos(user)
While this could be wrapped in the TodoService object like this:
TodoService.createTodo(username, password, todo_name, todo_priority)
TodoService.listTodos(username, password)
In this case it simply adds username/password to the service.
Frontend:
The frontend consists of Controllers, Commands and Validators. Much
like rails except that Validators are not directly tied to the domain
objects are (form) commands are plain objects. I like to seperate the
different concerns.
> I'd suggest that transaction demarcation is typically a UI thing
Many of my applications don't have a GUI. Or are simply a GUI on top of
a bigger system, a backend that works independent of the web app. So I
would say the GUI (the form's submit action) is one point of
transaction demarcation and strategies within an Application Logic
object are another place.
S.
More information about the Rails
mailing list