Archivist: Abstract Data Manager archetype for the Personify framework
An initial prototype for the Archivist archetype of the Personify reference model. An archetype is a meta-level pattern encountered in many different applications. Archetypes provide general functionality usually required in various contexts.
For example, applications often store data in databases. Traditionally, this meant Object-Relational Mapping (ORM). Such applications would often use an Active Record (e.g. Ruby on Rails) or a Data Mapper (e.g. Java Hibernate) to manage data persistence to long-term storage.
Personify manifests this as the Archivist archetype.
Like an archivist (a historian) in a castle, the Archivist archtype personifies the responsibility of storing information into long term storage.
An Archivist is responsible for knowing
- where to store things (An Archive)
** An Archive may optionally have a ValidationStrategy that corresponds to the schema of the table/validation rules for each record. For usage, see
- what to store (A Record)
An Archivist provides these basic services:
- Create(data) returns an Record with an id that wraps the data
- Read(id) returns the Record with the specified id, or an empty Record if none found
- Update(id, newData) returns the updated Record if newData validates. May throw a ValidationError if it does not
- Delete(id) returns the removed Record ** find(id) is an alias of read(id) ** remove(id) is an alias of delete(id)
A particular type of Archivist, for example a MongoDBArchivist, would extend the functionality of the Archivist archetype through an application of the Decorator pattern and explicitly devise its own Archives and providing their own implemenations.
Archives are recommended to correspond to Data Mappers, encapulating the persistence layer explicitly by exposing CRUD interface and then wiring up the underlying implementation accordingly.
Source Layout and contents
The directory content of Archivist adheres to the principles of Domain Driven Design by defining a Bounded Context called domain/ by convention. The domain may have separate modules, though it is a guiding principle that components be as minimalistic as possible. Hence, the Archivist has one module, archivist/.
Each module has an Aggregate Root, conventionally named for the Module (in this case, Archivist.coffee). The Module contains the standard DDD components of Models and Services. Factories and Repositories are option, though the beta applications in Personify have delegated those responsiblities to other Agents (the Archivist is the Repository handler). The Aggregate root exposes the agents the module provides for external interface. Modules in rzr style are Agents, though they may not always be very smart.
More on Agent Implementation
A dumb Agent may function much like an ideal Front Controller in a traditional Model-View-Controller application, forming an API for the services it provides and delegating requests appropriately.
More intelligent agents may use reasoning engines to examine their current context, ensure the safety and validity of the information they have been provided, and align their actions with their models of the world, their beliefs, and their goals.
Personify Agents are entities with stated contracts, similar to Design By Contract. These contracts are written in a Domain Specific Language for contract generation. These contracts outline their Beliefs, Capabilities, and Commitments. Beliefs expressed in the contracts become manifested in the Domain Model, which is updated and used by the Agent's Brain to update according to change in context and carry out actions.
The Archivist archetype is intentionally naive. Domain specific agents should decorate it with context-specific intelligence.
The Archivist archetype strives to be Simple and Easy, following Rich Hickey's definition. It provides simple CRUD to the external world.
Archivists really should not do more than this. Following the Single Responsibility Principle, they are intended to be specialized agents within an organization, requested by others to store data.
If feature functionality beyond the simple use case "Store Information X in Place Y by doing Z" starts to creep into your implementation, YOAR DOING IT WRONG. Refactor that logic elsewhere.