-
Notifications
You must be signed in to change notification settings - Fork 0
Reified Updates
Perhaps atypically, we do not update model entities with implicit sequences of set()
s, add()
s, put()
s, and delete()
s. Instead, we go about it explicitly and declaratively:
- we build an object that describes the required changes, a changeset.
- we ask the entity to apply the changeset, interpret it as a recipe for change.
We like changesets for a number of reasons, including:
-
they can travel over the wire in one go, as "batch updates".
-
we can put them in events to provide loosely-coupled, real-time services over the model. We`ve added changelog and validation services in this fashion: listening to updates and processing no more and no less than what's just changed.
What makes a good changeset for an entity? Another entity.
We don't model changesets as scripts of instructions. We describe instead the intended effects by building another entity of the same kind as the target, only made exclusively of the required changes.
So, if we want to change the name of a codelist, we build another codelist with the new name. If we also want to add a code to the first list, we add the new code to the second. And if we also want to modify an code of the first list, then we build a code changeset for it - with the same identifier and in the same spirit above - and then we add it to the second list. And so on (plus some provision for marking stuff to delete).
We have a Data
DSL to construct entities. We extend it just a little to construct changesets, e.g.:
String listId, codeId, antherCodeId;
QName newname;
...
Codelist changeset = modify(listId)
.name(newname) //new name
.with(
code().....build(), //new code
modify(codeId).....build(), //modified code
delete(anotherCodeId) //deleted code
).build();
#The Update API
Eventually, we land on this kind of update API:
Codelist list, changeset;
...
list.update(changeset);
We define and partially update()
along the trait hierarchy. Its covariant binary nature is largely responsible for the [use of generics in traits] (traits#whats-with-the-generics).
The update API includes also an auxiliary status()
method, which is primarily implementation-facing. It indicates whether the entity is a changeset (any non-null
value), and in case its enumerated Status
, i.e. whether it carries a change to an entity (Status.MODIFIED
) or the directive to delete it (Status.DELETED
). status()
is also implemented and mostly used in traits (Identified.Private
). It is also used in various domain services to process update notifications:
The update API is part of the private API of entities, and is one of the separation between public and private APIs. For good design reasons, API clients are limited to a few distinguished ones, primarily Repositories. All the other clients, update entities indirectly, via Repositories.