0

When applying DDD principles in Ruby, I feel that Active Record pattern ends up polluting the domain model, while I'm not sure how to implement JSON deserialization without breaking encapsulation.

The Data Mapper pattern could be an alternative, but I haven't found any satisfactory Ruby implementation that fulfills my Database requirements.

ActiveRecord usual implementation introduces persistence concerns on the domain entities, while separating domain entities and Active Record entities creates a duplicated class hierarchy, among other additional complexities.

With JSON deserialization, I either need to allow an empty constructor and public setters, or I need to give the Aggregate class the responsibility of deserializing JSONs, which doesn't seem as a legitimate domain concern, but more of an infrastructure concern. I understand this should be a Repository responsibility, but I don't see how to implement this without breaking the encapsulation of the aggregate class.

Perhaps the alternative would be to use Event Sourcing instead of persisting the aggregates states, or only persisting the aggregate state for read purposes. Is this correct?

  • What do you mean by "Active Record entities create a duplicated class hierarchy?" What are the other additional complexities? – Robert Harvey Jul 28 '17 at 1:59
  • 1
    Why is JSON deserialization an infrastructure concern, but database deserialization (i.e. loading an entity from the database) not an infrastructure concern? – Robert Harvey Jul 28 '17 at 2:00
2

I don't use Active Record in my projects as this pattern is forcing me to break my golden DDD rule: keep the aggregates pure, side-effect free.

If you do not split the Read from the Write, then you have a mixed aggregate. You need to be able to command it and query it. The problem is not querying a single aggregate, you can easily do that, after it is loaded from the repository, the problem is finding, filtering, browsing a list of aggregates because it forces you to break the aggregate encapsulation by depending on its internal structure. You need to know its structure in order to apply filters based on properties values. Every time you modify a property on the aggregate you need to adapt the filtering too.

If you split the Read from the Write (i.e. using CQRS and Event sourcing), then you can do all of the above without breaking aggregate encapsulation because you will not query the aggregate, you will query a special designated read-model, without breaking its encapsulation (because it applies its own filters on its own fields).

So, using Event sourcing helps a lot regarding your problem.

Of course, there are other problems that come with Event sourcing.

0

Disclaimer: not a ruby guy.

ActiveRecord looks to me like a trap. We want implementations that are easy to change. If our domain model is tightly coupled to the design of our persistence, then we need migrate our data each time we make a change to the data structures in the model? That doesn't sound like a good trade.

Conceptually, the representation that we store is a memento; a seed of data, written by our model in the past, which can be used to create a representation of the same state in the current model. The memento effectively plays the role of a message, which means lots of the lessons about designing message schema apply.

At the boundary between your domain components and your persistence components, you need two functions

  • a function that can take a model representation, and construct from it the corresponding memento
  • a function that can take a memento, and construct from it the correspoding model representation.

Event sourcing really doesn't help, because you face exactly the same issues -- you need to take the models representation of events and convert them to the storage representation, and vice versa.

Where did encapsulation go? At the boundaries, applications are not object oriented.

The only alternative approach I know: eliminate the objects. If you examine the state, rather than the behavior, a state change looks something like

State current = database.getState()
State next = model.transform(current)
database.replace(current, next)

If you give the middle line a twist

State current = database.getState()
State next = current.bind(model.transform)
database.replace(current, next)

And the whole thing starts to look like some sort of DB Monad

DB<State> current = database.getState()
DB<State> next = current.bind(model.transform)
database.replace(current, next)

A way to envision this is that the application is passing to us a Command, and what we are really doing is translating it to a Command -- much in the same way that an ORM takes a bunch of state changes and magics them into SQL commands.

That doesn't make implementing the code any easier, as far as I can tell.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.