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
#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
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
#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
I've been calling this the data triad. I also refer to these components with the following equation.
D = Ma + Mo + A