Written by Luke Morton on 25th September 2013

Some thoughts on the data triad. That is mappers, models and actions.

The data triad is all about setting and getting data to and from data sources. The first part of the triad, mappers, are all about communicating with data sources.

So as you can see mappers do a fair bit of the leg work. They encapsulate communication with a data source by providing a number of methods.

The example above takes a hash of data on construction and it's method selects or inserts hashes from that hash.

Data models use mapper methods to select data. They then model this data to be consumed. In a web application a model would be called by a controller and used to determine the outcome of a request. The controller can also pass the modelled data to view models for further view related transformation.

You might have a data model to get user data for a profile.

We load a user hash by calling #find_one_by_id on an injected mapper. We then have the replacement of user[:friends] with a selection of other user data using the injected mapper's #find_by_ids. The completed user hash is then returned as part of another hash.

As you can see data models are used to build up data and model it for consumption. Usually for a specific use case.

So you might have guessed what data actions do. They act upon data sources via data mappers. They insert, update and delete.

So I included a couple more classes to make this a complete example. You shouldn't really put data into a source without validating it first you know!

The main point of discussion however is RegisterUserDataAction. It uses a validator to ensure the data is good to insert. If it isn't it'll encapsulate the validator in an error and raise it. Alternatively you could return a hash of error data. If the data was fine this will then get passed to #insert, another mapper method. You might also send welcome emails at this point which would happen on a service injected into the #exec method just like the mappers.

Brief roundup

So that's that. A quick summary:

  • mappers act on data sources
  • models use mappers to get and model data
  • actions use mappers to set data, usually with validation

Just like the separation of concerns with T + M + TE we get all the goodness of modular design.

Mappers define a source agnostic interface for communicating with data sources. They should be written agnostically so you can replace them with alternate mappers for other data sources. They should always be injected into models and actions so you can replace them. Mappers could potentially interface with ActiveRecord, ROM or more directly with data layers such as Sequel or Mongo.

Models aren't your ActiveRecord model. They take in mappers and data for querying and return a hash model. This is why the method is called #to_hash after all. Models are directly coupled to the mapper methods they call. This is to be expected and of course the model only exposes one public method so the mapper implementation is hidden from the application controller or where ever your model is used.

Actions also aren't your ActiveRecord model. They take in mappers and data to insert, update or delete records in a data source. They too, like models, are directly coupled with the mapper methods but again only have one public interface themselves #exec. Ideally actions will validate the data before acting on it. They needn't be tied to the validator class like the example above, of course this could have also been injected.

Final note

I've been calling this the data triad. I also refer to these components with the following equation.

D = Ma + Mo + A

Also I've previously written about the view triad. Let me know what you think of the D or the V that I've written about @LukeMorton.


Feel free to read some more thoughts or go back to the introduction.