Builders
In the part 1, the code snippet about the controller has a line
1
|
|
This implies that a Builder Pattern is used for creating a model, more precisely, creating an “Aggregate” in the Domain Driven Design terminology. The return value of a builder should always be the root object of the aggregate. Below is an example of a build
method.
1 2 3 4 5 6 7 8 9 10 11 |
|
A DRY version of a typical builder in the form of an internal DSL could be:
1 2 3 4 5 6 7 |
|
A lof of Rails projects I have seen have building email_addresses
and phones
logic in the UsersController
, which then becomes hard to unit test and impossible to reuse.
Another replacement to the builders is using hooks, such as after_create
. This makes your code highly depends on a specific ORM implementation, making the one line user.create!
becomes hard to understand, and slow the unit tests.
The User BusinessModel should be responsible building a user
and only it. Below is a possible implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
This, as usual, can be abstracted into a DSL:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Business Models
A business model, in Domain Driven Design term, is a Entity. A business model should be persisted, but the model itself knows nothing about how itself being persisted, it delegates such a task to its persistence class.
A business model, contains attributes and business-related calculations based on these atrributes.
A business model also contains the validations that can be called by user.valid?
Persistence Classes
In the controller code in the part 1:
1
|
|
A Persistence class takes the business model object as its only parameter in its constructor and the persists the object. The Persistence object knows to how to map a model object’s attributes into database columns, for example. An example of such a method could be like this:
1 2 3 4 5 6 7 8 9 |
|
Buidling a business model from the persistence layer might look like this:
1 2 3 4 5 6 7 8 |
|
The ActiveRecordStore::User
in the code could be smart enough to load a user based on what attributes in the business_object
. For example:
1 2 3 4 5 6 7 8 |
|
The code bears some explainations: physical_attributes
is a method to transform the business model object attributes into the table columns. This method is used by both save
and load
. The Physical::User
is a child of ActiveRecord::Base
that talks to the database directly.
Some custom methods might be needed, such as find_by_email_address
. These kind of finders, though, can be easily standardized.
1
|
|
DSLs
All the above examples can often be DRYed up by some internal DSLs. But I feel that I must say that these DSLs should not be overused.
A sympton of overuse of DSL (and thus over abstract the logic) is that the DSL allows too much options. When you find out you have to handle more than 2 slightly different situations in one DSL implementation method, it is time to seperate the situations into 2 or more slightly different DSL macros. And of course these DSL macros implementations can share a large portation of logic by abstract the common code out.