- Our repositories work with aggregates (entities). This is, rich domain models rather than plain anemic models. The repository performs database queries and then pass in the raw results to the aggregate's constructor or to some adapter to build up the model. Thus the repository returns models to the service layer and receives models.
- Currently an object is a DTO only if it's used to send and receive data through the network (via HTTP requests).
- Bad smell: the fact that only certain fields of a DTO are used in some part of the application and the other fields are used in another place. That means we are trying to reuse concepts that are different. The solution is to split the object in two. Principle: a data object have to be consistent, all its fields must be in use.
- The objects we bind to the GUI are not called DTOs anymore. But we are not happy calling them ViewModels either, because we still want to make the distinction between a data object bound to the GUI, and a kind of "Controller" that manages GUI logic and depends on collaborators like application services. So we are avoiding prefixes and suffixes for those objects and using their namespaces to distinguish them. For example if "Vehicle" happens to have the same properties in the model, the communications layer and the GUI, there will be three objects:
- The transformations between DTO and Model are performed by an adapter as described in Part I. But we do not create the three objects from the very beginning when there is just one object with one field or two. We may use the same object all over the place until it grows and starts acquiring some logic.
- We usually talk about "bindable objects" to refer to those data objects bound to the GUI. Sometimes the tranformation between domain model and bindable object is performed by some ViewModel/Controller. Sometimes we delegate the task in an adapter, within the ViewModel namespace.
- Transfer the minimum amount of data for each operation, no more. A single service may work with many different DTOs because each operation requires different data sets. For instance, the OfferService (an application service) has public methods to create and update offers. The OfferDto used to contain all the data required for both operations:
public void Create(OfferDto offer);
public void Update(OfferDto offer);
However only a subset of the data is required for each operation. Now we prefer to exchange a minimal amount of data:
public void Create(NewOffer offer);
public void UpdatePrice(OfferId offerId, decimal price);
public void Update(UpdatedOffer offer);
This approach help us realize those objects are used just to transfer data, nothing more.
- Keep data structures simple in the client/GUI side: if all I need of some entities are their identities, I don't need whole objects in that view, I can just hold a list of strings. Once the strings enter the hexagon, they'll be turned into domain models.
- Keep the outer interface of the hexagon simple so as to make it simple to interact with: it's OK to receive primitives as arguments in our application services or actions.