5

I'm learning DDD (domain driven design) and the repository pattern (in C#). I would like to be able to use the repository pattern to persist an entity and not care which database is actually used (Oracle, MySQL, MongoDB, RavenDB, etc.). I am, however, not sure how to handle the database specific id:s most (all?) databases uses. RavenDB, for example, requires that each entity it should store has an id property of type string. Other may require an id property of type int. Since this is handled differently by different databases, I cannot make the database id a part of the entity class. But it would have to exist at some point, at least when I store the actual entity. My question is what the best practise regarding this is?

The idea I am currently pursuing is to, for each database I want to support, implement database specific "value objects" for each business object type. These value object would then have the database specific id property and I would map between the two upon reads and writes. Does this seem like a good idea?

David Nordvall
  • 12,404
  • 6
  • 32
  • 52
  • 1
    Do you really need it? Seems as not good idea for me because of maintenance hell you will encounter. – xelibrion Aug 12 '11 at 05:26
  • 1
    I'm facing the same problem. Unfortunately, none of the solutions mentioned here stroke me as "the perfect solution" (if there is one ...). I also considered casting to the specific implementation type within the data abstraction layer. What I mean is that while the `DTO`s and `DAO`s are interfaces common to all DB types, any specific `DAO` implementation will cast the `DTO`s inside its methods to that same specific implementation type (I'm assuming only one DB type is used at a time). This way, it will be able to use the specific ID. It seems dirty, but it's the best I came up with so far. – ethanfar Sep 03 '14 at 05:24
  • I just found another question regarding something similar, to which there's an answer that seems like it might work here as well. Take a look at: http://stackoverflow.com/questions/10785598/abstracting-an-id-attribute-in-c-sharp-or-java – ethanfar Sep 03 '14 at 06:07

4 Answers4

3

This is the classic case of leaking abstractions. You can't possibly abstract away the type of database under a repository interface unless you want to loose all the good things that come with each database. The requirements on ID type (string, Guid or whatever) are only the very top of huge iceberg with majority of its mass under the muddy waters.

Think about transaction handling, concurrency and other stuff. I understand your point about persistence ignorance. It's a good thing for sure to not depend on specific database technology in the domain model. But you also can't get rid of any dependency on any persistence technology.

It's relatively easy to make your domain model work well with any RDBMS. Most of them have standardized data types. Using ORM like NHibernate will help you a lot. It's much harder to achieve the same among NoSQL databases because they tend to differ a lot (which is very good actually).

So my advise would be to do some research on what is the set of possible persistence technologies you will have to deal with and then choose appropriate level of abstraction for the persistence subsystem.

If this won't work for you, think about Event Sourcing. The event store is one of the least demanding persistence technique. Using library such as Jonathan Oliver's EventStore will allow you to use virtually any storage technology, including file system.

Szymon Pobiega
  • 3,358
  • 17
  • 18
  • 2
    No, this is the opposite - a classic case of avoiding leaky abstractions. Using the store's ID type as your global 'friendly' reference for an object is leaky. Making the store persist that ID is fine & usually necessary. Usually the 2 things happily coincide. This chap is trying to separate the two, hence he must resolve this issue. – Chalky Jul 15 '14 at 23:23
  • @Chalky I agree with you, there's (almost always) no such thing as too much abstraction when it comes to major API providers such as a DB. If you rely on the DB type so much that you use the specific ID type everywhere in your code, you're not going to be able to change the DB for ... well, practically forever ! Companies are reluctant to replace the DB they're using with another once the DB is full of data is also largely because most of the time, it also means providing a completely new implementation of the software, due to the type of massive coupling caused by assuming a specific ID type. – ethanfar Sep 03 '14 at 05:31
1
  1. You are doing the right thing! Abstract yourself away from the constraints of the databases primary key types!

  2. Don't try to translate types, just use a different field.

Specifically: Do not try to use the database's primary key, except in your data access logic. If you need a friendly ID for an object, just create an additional field, of whatever type you like, and require your database to store that. Only in your data access layer would you need to find & update the DB record(s) based on your object's friendly ID. Easy.

Then, your constraints on which databases can persist your objects have changed from 'must be able to have a primary key of type xxxx' to simple 'must be able to store type xxxx'. I think you'll then find you cna use any database in the world. Happy coding! DDD is the best!

Chalky
  • 1,624
  • 18
  • 20
  • I'm having the same problem in the question as well. What you suggest is the first thing that came to my mind. The only thing that bugs me about it though, is that it might create two different ID type fields in pretty much any object in the DB. – ethanfar Sep 03 '14 at 05:34
1

I would go ahead and create an int Id field in the entity and then convert that to a string in the repository where the Id must be a string. I think the effort to abstract your persistence is very worth while and actually eases maintenance.

cdaq
  • 167
  • 1
  • 9
0

You can potentially have the ids in the entity but not expose it as part of entity's public interface. This is possible with NHibernate because it allows you to map table column to a private field.

So you can potentially have something like

class Customer {
        private readonly Int32? _relationalId;
        private readonly String? _documentId;
        ...

This is not ideal because your persistence logic 'bleeds' on business logic but given the requirements it probably is easier and more robust than maintaining mapping between entity and its id somewhere outside entity. I would also highly recommend you to evaluate "Database agnostic" approach which would be more realistic if you only want to support relational databases. In this case you can at least reuse ORM like NHibernate for your repository implementation. And most relational database support same id types. In your scenario you not only need ORM you also need something like "Object-Document-Mapper". I can see that you will have to write tons and tons of infrastructure code. I highly recommend you to reevaluate your requirements and choose between relational and document databases. Read this: Pros/cons of document-based databases vs. relational databases

Community
  • 1
  • 1
Dmitry
  • 17,078
  • 2
  • 44
  • 70