Responsibility: persist entities to a database/storage and retrieve them later. Often called a repository.
A few important rules apply to the persistence layer:
they contain zero business logic
their input is one or multiple domain entity or a domain-specific change description
their output is one or multiple domain entity or a command feedback
Persistence modules do not create entities, that is the role of Services. There should be no "create" method in them. Persistence only stores things.
The API interface of persistence modules talks the language of the domain and never mention storage-specific information.
It is fine to use a framework, library or 3rd party service as a repository. But they have to remain hidden behind a bespoke, domain-driven API. Looking at the interface should reveal nothing about the technology behind the storage layer.
Add an explicit API interface type to your codebase. This way, different implementations can exist at the same time. A "real" one can save to the database of choice, another to memory for testing.
Keep in mind that the domain services define the API contract for repository modules. There is an obvious shortcut: import the definitions from the services into persistence.
No business logic exists in the persistence layer. These modules are storage boxes. They can store an entity and later return it in a pristine state, but they will never mutate it. The way they achieve this goal is an implementation detail.
There is no validation implemented in the repository - that is business logic. The incoming payload is validated by the services.
Suggestion: use an ID generated by the domain services instead of an auto-generated one from the DB. Your IDs decouple you from database-specific ID formats. The entity shapes will be more straightforward (they always have an ID). There will be no need to change the ID field names either (from
The Persistence API is not public and will import only from the domain or service modules.
The general rules about API and separation of concerns apply to any other IO modules we need to inject into the services: email or SMS sending, logging, and so on.