问题
I realize that persistence frameworks such as Morphia and Hibernate rely on annotations on domain objects to do their magic. At some level, it seems to me that this is inserting persistence concerns into the domain layer which is something we're supposed to strive to avoid.
Is this something that I should try to dodge by perhaps using an external configuration file or maybe separate DTOs from the domain model? Or is this little leak between the persistence and domain layers generally regarded as acceptable?
回答1:
In my latest iteration on an existing system using Spring and Hibernate, I have started to move in a similar matter. When first implementing Hibernate models, I strove to separate the application logic in service classes from the persistence logic via data access objects. When building a new system last year I allowed most of the persistence objects to serve as the domain objects because that was the expedient solution.
However, I am redesigning this same system in light of changing business requirements, and I'm again leaning towards separating those concerns. I'm only a few days into the new design, but already I find myself preferring to have one object that represents the in-memory concerns object while a separate persistence-based object is used to store its state changes to the database. For example, I have a Lead
for persistence and a parallel ActiveLead
that lives across transactions.
I'm not yet convinced this is the best method, but it makes sense on a gut level. I've longed to have a collection of persistence-agnostic--nay, persistence-ignorant--set of objects that remain memory-resident across database transactions without regard to the standard CRUD simplifications. Yet I understand that in the end all database operations are implemented as CRUD. The two worlds must collide, and the trick is in making them dance in harmony.
Hibernate annotations on domain objects? This is a fine compromise between ease of implementation vs. ease of maintenance in my view.
回答2:
Are persistence annotations in domain objects a bad practice?
Yes. With the rise of NoSQL, you can not rely on single persistence strategy.
For example, today I am persisting my domain objects (let's say using morphia) into MongoDB. What if tomorrow I want to persist domain objects to Neo4j ?
Or, one might want to persist domain objects to all three kinds of databases like relational (Postgres / MySQL), MongoDB(document store) and Neo4J(graph database) just for evaluating.
In all these cases, better to have separate persistence strategy rather than just relying on domain objects
Best Practice : Passing Persistent strategy as a strategy pattern might help. But have to be careful while designing your classes / objects.
回答3:
I've recently worked on a reasonably complex system with had a separate persistence layer, and it was a huge pain in the ass and very bad for maintainability. You're basically looking at a conflict between the principles of YAGNI and Single Responsibility. In my opinion, YAGNI is the more important one (alas, also the more frequently ignored one).
I'd say in the vast majority of cases, it's much better to persist domain objects directly if you're using an ORM, unless you have concrete requirements that force the persistence entities to be structured differently (if they have exactly the same structure, there is no reason to separate them except ivory tower arguments).
To be sure: always do the actual persistence stuff (calling ORM functions) in a separate service/DAO layer! That way, it's easy to introduce a persistence layer later if you find that you need it.
回答4:
I believe I will use annotations on my domain if I am already decided with the persistence framework I am going to use, however, XML would be more convenient if you follow the Hexagonal architecture and TDD. If you annotate your domain with specific framework upfront, your will be coupled with the persistence integration and unable to test the core functionality with the aim of being technology/framework agnostic.
回答5:
In my opinion there's no need to duplicate the domain objects to be able to seperate them from your persistance layer. It works duplicate code in hand and it's perfectly doable by using those same objects as DTO's. If necessary you can always use seperate classes if it's needed, but I wouldn't make it a rule of thumb, it 'll cost you time and we all know time is valuable.
回答6:
Short answer: I like persistent, rich, domain objects.
Long answer:
For nearly 10 years I worked on a fairly large system ~ 500k LOC using Spring and Hibernate. At the beginning, we started with a "transaction script" (see Fowler) approach, partly because we didn't quite trust Hibernate. However, in a short time, we came to trust Hibernate and due to my much earlier training in fairly purist OO, I became a big believer in transient persistence combined with a Domain Driven Design approach. We basically came to think of our system as being backed with an ODBMS (with plenty of small leaks :-)).
I called our architecture a "domain kernel" because the book DDD had not been written yet. This was in the early days of Hibernate, so the domain model was not polluted with annotations. The separate concern of persistence was kept separate in XML mappings.
Again, over time, we got better at pushing behavior down into the domain layer. We had a pretty traditional controller-->service-->dao-->domain layering scheme that was enforced with compile-time dependencies. What I observed over time is that this model worked very well for our system, which represented every facet of the fairly complex domain of 401(k) plan management, including plan setup, trading, accounting, compliance testing, sales, branding, etc. A rich domain model with (relatively) transparent "magical" persistence was key in our being able to build new features in terms of existing features in the domain model.
Our service layer only orchestrated the interactions between technical services (such as email, file I/O, queuing, etc.) and helped to span domain packages when necessary. The service layer also defined the transaction boundary (via Spring). Services only received or emitted DTOs or primitives. A lot of people hate that, as a break in DRY, but we found it kept us honest when defining the service interfaces and the code that used them. It also made it very easy to remote things later.
This approach allowed us to build high-quality software with a pretty small team (we were a Scrum team).
So, consider me a believer in persistent domain objects. Don't know if my story helps, but I wanted to share.
回答7:
I would prefer rich domain objects which have the annotations on it. Even Evans uses this approach in his sample app. He uses XMl instead of Annotations but he still persists the same objects.
Maybe it´s more clean to separate domain and persistence but don´t do just to be able to potentially choose a different db technology in the future. It´s the way down to complexity hell and Mister YAGNI will bite you.
回答8:
Something found in DDD community
Post by Chris Richardson * If you want to keep JPA out of the domain model then use XML instead of annotations (i've never been a fan of ORM annotations since IMHO it mixes concerns)
Personally I like a lot to use annotations, XML was always for me error-prone, one small change in a field name and you need to manually change XML too. If you want to refactor a single class of your domain, you could end up changing several files instead of it being handled automatically. But lately, I'ven been reconsidering this because I want to be able to use several persistent options in a project. I do not want anything related to persistence in my domain, so XML is an option. Still, several times I get to a point where there is no direct mapping, or I still want to use annotations because they are so easy to change and visible right into the code. Somethinig I've been doing lately is create my business domain class as and abstract one, and user another for persistence extending it. Something like this:
public abstract class Persona {
private Set<State>states;
public boolean inState(State state){
return states.contains(state);
}
}
if, for some reason there is a db where states are already defined as a single column, and no direct mapping is possible, I can extend business class and used it as a persistence entity.
@Entity
public class PersonaSql extends Persona {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String statesDefinition;
@PrePersist
void prePersist(){
this.statesDefinition = mapStatesSetToString();
}
@PostPersist
void postPersists(){
this.states = mapStatesStringToSet();
}
}
Of course, this is a trivial example. There are other ways to solve this issue, my point is: by using inheritance you can take the great advantages of working with annotations and have your business model ignorant of specific persistence code.
Another option without using inheritance is converting persistence entity to business model and viceversa, but I won't recommend going this route (even with things like automapper), unless your domain is simple and you are sure it is going to stay simple. For example, if you are creating micro-services, your domain should be simple enough and its expected to be simple.
来源:https://stackoverflow.com/questions/10099636/are-persistence-annotations-in-domain-objects-a-bad-practice