Let's say I have a domain java class representing a person:
class Person {
private final String id; // government id
private String name;
private String status;
private Person(String id, String name) {
this.id = id;
this.name = name;
this.status = "NEW";
}
Person static createNew(String id, String name) {
return new Person(id, name);
}
void validate() {
//logic
this.status = "VALID";
}
public static final class Builder {
private String id;
private String name;
private String status;
private Builder() {
}
public static Builder aPerson() {
return new Builder();
}
public Builder id(String id) {
this.id = id;
return this;
}
public Builder name(String name) {
this.name = name;
return this;
}
public Builder status(String status) {
this.status = status;
return this;
}
public Person build() {
Person person = new Person(id, name);
person.status = this.status;
return person;
}
}
I store this domain class object in a database, regular class with the same field + getters and setters. Currently when I want to store object I create new PersonDocument
(data is stored in mongo), use getters and setters and save it. It gets complicated when I want to fetch it from DB. I would like my domain object to expose only what is necessary, for the business logic currently it is only creation and validation. Simply:
Person p = Person.createNew("1234", "John");
p.validate();
repository.save(p);
The other way it gets complicated, currently there is a builder which allows creation of object in any state. We do believe that data stored in DB has a proper state so it can be created that way but the downside is that there is a public API available, letting any one to do anything.
The initial idea was to use MapStruct java mapping library but it does use setters to create objects and exposing setters in the domain class (as far as I can tell) should be avoided.
Any suggestions how to do it properly?
Its a state of a Domain object. a persistent instance has a representation in the database and an identifier value. It might just have been saved or loaded, however, it is by definition in the scope of a Session. For example see the states of objects here in java ORM framework hibernate.
An entity is a lightweight persistence domain object. Typically, an entity represents a table in a relational database, and each entity instance corresponds to a row in that table. The primary programming artifact of an entity is the entity class, although entities can use helper classes.
In Java servers, persistence layer is also known as the repository layer. This layer is responsible for data persistence and is used by the business layer to access the cache and database. In terms of functionality, it is fine to write the persistence layer code in the service class.
Persistence Layer = generally means to isolate read/write/delete logic from the business logic. ideally by placing a few (or a single) point of interaction between the business logic and the persistence modules.
Your problem likely comes from two conflicting requirements:
One of those has to give. To be honest, most people faced with this problem ignore the first one, and just introduce setter/getters. The alternative is of course to ignore the second one, and just introduce the serialization/deserialization into the object.
For example you can introduce a method Document toDocument()
into the objects that produces the Mongo compatible json document, and also a Person fromDocument(Document)
to deserialize.
Most people don't like this sort of solution, because it "couples" the technology to the object. Is that a good or bad thing? Depends on your use-case. Which one do you want to optimize for: Changing business logic or changing technologies? If you're not planning to change technologies very often and don't plan using the same class in a completely different application, there's no reason to separate technology.
Robert Bräutigam sentence is good:
Two conflicting requirements
But the is another sentence by Alan Kay that is better:
“I’m sorry that I long ago coined the term “objects” for this topic because it gets many people to focus on the lesser idea. The big idea is messaging.” ~ Alan Kay
So, instead of dealing with the conflict, let's just change the approach to avoid it. The best way I found is to take a functional aproach and avoid unnecessary states and mutations in classes by expresing the domain changes as events.
Instead to map classes (aggregates, V.o.'s and/or entities) to persistence, I do this:
Persistence.Apply(event)
). This way your persistence knows what was happened and; as long as the event has all the data needed to persist the change; can apply the change into (even with behaviour if needed!).Check this post (it worth chek all DDD series in this blog) to see a similar implementation.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With