Repository design - Pyosch/powertac-server GitHub Wiki
Requirements for server data storage
According to Eric Evans, the software design universe can be understood as a collection of Entities, Values, and Services. Entities typically correspond to domain objects, are distinguished by a unique identifier (at least among entities of the same type), and may have internal state that evolves over time -- they are typically mutable objects. Values are distinguished by state -- they are typically immutable, and often used as data carriers between services and entities. Services may or may not have state, but they are typically Singletons, and exist primarily to represent elements of business processes.
In the PowerTAC simulation, Entity types include Customers, Brokers, Tariffs, Orderbooks, etc. Value types include TariffSpecifications, the various Transaction types, etc. Value objects include most of the message types that are communicated between brokers and the server; probably all messages should be Value types. Services are the auction market, the tariff market, the competition controller, etc. Service types map nicely to the Grails notion of Services.
Because Services are singletons, there is no need to "store" them - they can be looked up by class, and can be auto-wired by Spring. Entities and Values commonly need to be retrieved by various services, either by ID or by attributes. Some of the retrieval activity in the Grails server is done simply to avoid stale objects, but even leaving these instances aside, there are many cases that require some sort of query processing.
So the requirements for domain object storage (entities and values) are relatively simple:
- Objects in storage can be created, updated (in the case of Entity types) or deleted.
- Retrieval service is visible or easily accessible from relevant services; typically this implies either static methods on domain object classes, or one or more Repository services.
- Query methods must be available as needed, but a general query language facility is not needed.
- Creating, updating, and deleting of domain objects must be logged in a way that allows complete reconstruction of server scenarios. Note that it is not required that the database be dumped at the end of a scenario, as long as all the objects in it have already been logged.
- At the end of a scenario, there must be a simple, reliable way to clear out the database in preparation for the next run.
Alternatives to relational database storage
The relational database used in the original PowerTAC server has been a source of numerous problems, although it has been effective in making data available through simple queries. There are three obvious alternatives available for storage and retrieval of domain types:
- Static methods on domain classes. Several domain classes already have static retrieval methods that use the database; these would be easy to convert.
- Centralized repository, to more or less mimic the architecture with a single database.
- Modular repositories, focused on major domain concepts. Evans calls these "aggregates".
It's hard to see the value or applicability in the centralized approach for a modular server. The need for a simple way to clear out the database argues against the static approach, unless we couple each such class with an InitializationService. Therefore, it seems that the best option is modular repositories, each of which is also an InitializationService.
Factory-finder convention
Another name for a modular repository is "factory-finder". With a simple abstract class and a set of naming conventions, the refactoring process can be relatively painless. The abstract class would be called Repository, and would implement InitializationService, along with methods add()
, list()
, and findById()
. Because ID values can be either int or String, we will need polymorphic versions of add()
and findbyId()
. The concrete Repository types would be named after their base types with a suffix Repo
for brevity. Each would be annotated as a Spring service.