Question

Okay, I'll be straight with you guys: I'm not sure exactly how Domain Driven my Design is, but I did start by building Model objects and ignoring the persistence layer altogether. Now I'm having difficulty deciding the best way to build my tables in SQL Server to match the models.

I'm building a web application in ASP.NET MVC, although I don't think the platform matters that much. I have the following object model hierarchy:

Property - has properties such as Address and Postcode

 which have one or more

Case - inherits from PropertyObject
Quote - inherits from PropertyObject

 which have one or more 

Message - simple class that has properties Reference, Text and SentDate

Case and Quote have a lot of similar properties, so I also have a PropertyObject abstract base class that they inherit from. So Property has an Items property of type List which can contain both Case and Quote objects.

So essentially, I can have a Property that has a few Quotes and Cases and a load of Messages that can belong to either of those.

A PropertyObject has a Reference property (and therefore so do Quote and Case) so any Message object can be related back to a Quote OR Case by it's Reference property.

I'm thinking of using the Entity Framework to get my Models in and out of the database.

My initial thoughts were to have four tables: Property, Case, Quote and Message.

They'd all have their own sequential IDs, and the Case and Quote would be related back to Property by a PropertyID field.

The only way I can think of to relate a Message table back to the Case and Quote tables is to have both a RelationID and RelationType field, but there's no obvious way to tell SQL server how that relationship works, so I won't have any referential integrity.

Any ideas, suggestions, help?

Thanks, Anthony

Was it helpful?

Solution

I am assuming Property doesn't also inherit from PropertyObject.

Given that these tables, Property, Case, Quote and Message, leads to a Table per Concrete Class or TPC inheritance strategy, which I generally don't recommend.

My recommendation is that you use either:

  • Table per Hierarchy or TPH - Case and Quote are stored in the same table with one column used as a discriminator, with nullable columns for properties that are not shared.
  • Table per Type or TPT - add a PropertyObject table with the shared fields and Case and Quote tables with just the extra fields for those types

Both of these strategies will allow you to maintain referential integrity and are supported by most ORMs.

see this for more: Tip 12 - How to choose an inheritance strategy

Hope this helps Alex

OTHER TIPS

Ahhh... Abstraction.

The trick with DDD is to recognize that abstraction is not always your friend. In some cases, too much abstraction leads to a too-complex relational model.

You don't always need inheritance. Indeed, the major purpose of inheritance is to reuse code. Reusing a structure can be important, but less so.

You have a prominent is-a pair of relationships: Case IS-A Property and Quote IS-A Property.

You have several ways to implement class hierarchies and "is-a" relationships.

  1. As you've suggested with type discriminators to show which subclass this really is. This works when you often have to produce a union of the various subclasses. If you need all properties -- a union of CaseProperty and QuoteProperty, then this can work out.

  2. You do not have to rely on inheritance; you can have disjoint tables for each set of relationships. CaseProperty and QuoteProperty. You'd have CaseMessage and QuoteMessage also, to follow the distinction forward.

  3. You can have common features in a common table, and separate features in a separate table, and do a join to reconstruct a single object. So you might have a Property table with common features of all properties, plus CaseProperty and QuoteProperty with unique features of each subclass of Property. This is similar to what you're proposing with Case and Quote having foreign keys to Property.

  4. You can flatten a polymorphic class hierarchy into a single table and use a type discriminator and NULL's. A master Property table has type discriminator for Case and Quote. Attributes of Case are nulled for rows that are supposed to be a Quote. Similarly, attributes of Quote are nulled for rows that are supposed to be a Case.

Your question "[how] to relate a Message table back to the Case and Quote tables" stems from a polymorphic set of subclases. In this case, the best solution might be this.

Message has an FK reference to Property.

Property has a type discriminator to separate Quote from Case. The Quote and Case class definitions both map to Property, but rely on a type discriminator, and (usually) different sets of columns.

The point is that the responsibility for Property, CaseProperty and QuoteProperty belongs to that class hierarchy, and not Message.

This is where the DDD concept of Services would come in. The Repository for each of your concrete classes only persist that entity, not the related objects.

So you have Property(), and is the base for your CaseProperty() : Property(). This special-entity is accessed via CasePropertyService(). Within here is where you would do your JOINs and such to the related tables in order to generate your CaseProperty() special entity (which is not really Case() and Property on its own, but a combination).

OT: Due to limitation of .net of where you can't inherit multiple classes, this is my work around. DDD is meant to be a guideline to the overall understanding of your domain. I often give my DDD outline to friends, and have them try to figure out what it does/represent. If it looks clean and they figure it out, it's clean. If your friends look at it and say, "I have no idea what you are trying to persist here." then go back to the drawing board.

But, there's a catch about using any ORM to persist storage of DDD objects (linq, EntityFramework, etc). Have a look at my answer over here:

Stackoverflow: Question about Repositories and their Save methods for domain objects

The catch is all objects must have an identity in the database for ORM. So, this helps you plan your DB structure.

I have recently moved away from using ORM to control direct access, and just have a clean DDD layer. I let my repositories and services control access to the DB layer, and use Velocity to entity-cache my objects. This actually works very well for: 1) DB performance, you design however is most efficient not being coupled to your DOmain objects with direct ORM representation, and 2) your domain model becomes much cleaner with no forced identies on Value Objects and such. Free!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top