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.