Assume an application architecture with three layers (presentation, domain, data access - though presentation is irrelevant to this question) that follows dependency inversion:
- The domain layer specifies domain models and repository interfaces
- The data access layer has a reference to the domain layer, implements repositories for a given storage provider, and uses data models internally
- Repository interfaces use domain models, the domain layer does not reference the data access layer and is not aware of data models
- The data access layer maps domain objects to data objects and the other way around
One goal of the architecture is to be able to exchange storage provider implementations by just reconfiguring the DI container, without any changes in the domain layer. The domain layer should not hold a reference to the data access layer.
My question: How do you deal with implementation details of specific data access providers that are required for the data layer to do its work correctly, without the domain layer having to know about data access implementations?
Example: Azure Cosmos DB uses ETags for optimistic concurrency control. When the data object is mapped to a domain object, the ETag is lost (because the domain model doesn't know about the ETag, it's an implementation detail of the data access layer). If the domain layer changes properties on the domain object and passes it back to the repository's update method, concurrency control is broken because the ETag value has been lost. Other storage providers use different mechanisms for concurrency control (like DB row locking). My goal is for the domain layer to not have to know about such implementation details.