In the part 1, the code snippet about the controller has a line
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
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
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
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
In the controller code in the part 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
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
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.
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.