My question is very simple: How do get my class's private data to the Repository to save?
Regardless of the architectural style we adopt, everyone agrees that business objects shouldn't know "how" to save themselves - that is they shouldn't implement database or other persistence details. However, it seems to me that business objects are the only ones who know "what" they need to save. The Repository knows how to fetch from the database, but if it is to know how to translate a business object into database terms, then it must know what to translate.
Consider that I may use a database, but I'm not marking my classes up with hibernate annotations because I might just as often save to a flat text file.
Assuming my classes here are actually named after specific business entities, What would be wrong with doing something like
interface Exporter
{
public void Export(String id, String value1, String value2);
}
interface Repository
{
public Entity FindById(String id);
}
class Entity
{
private String id;
private String value1;
private String value2;
private String derivedvalue;
public Entity() {}
public Entity(String id, String value1, String value2)
{
this.id = id;
this.value1 = value1;
this.value2 = value2;
this.derivedvalue = /* derived from id, value1, and value2 */;
}
public void DoBusiness()
{
// ...
}
public void Export(Exporter exporter)
{
Exporter.Export(this.id, this.value1, this.value2);
}
}
and using it like
FlatFileRepositoryAndExporter flatfile = new FlatFileRepositoryAndExporter(...);
Entity entity = flatfile.FindById(...);
// Do business logic
entity.DoBusiness();
entity.Export(flatfile);
I understand there are frameworks that can assist me, but at the end of the day, they all rely on reflection of some sort. I want to know, without reflection, how can I statically compose my objects to expose their data while maintaining encapsulation. The only answer I can come up with is this visitor pattern.
I tend to agree with @MikeSW that making external persistence tools able to collect domain object state, with the help of minor scope adjustments (or reflection as ORMs do), is often simpler than letting the domain object itself control entirely what is persisted.
There's a third way which consists in making the entity emit events describing what happened instead of exposing its state -- that's Event Sourcing. Then whoever wants can listen to those and persist changes in whatever form they want to derive.