views:

69

answers:

2

I've been following a mostly DDD methodology for this project, so, like any DDD'er, I created my domain model classes first. My intention is to use these POCO's as my LINQ-to-SQL entities (yes, they're not pure POCO's, but I'm ok with that). I've started creating the database schema and external mapping XML file, but I'm running into some issues with modeling the entities' relationships and associations.

An artifact represents a document. Artifacts can be associated with either a Task or a Case. The Case entity looks like this:

public class Case 
{
     private EntitySet<Artifact> _Artifacts;
     public IList<Artifact> Artifacts
     {
          get
          {
               return _Artifacts;
          }
          set
          {
               _Artifacts.Assign(value);
          }
     }
     .
     .
     .
}

Since an Artifact can be associated with either a Case, or a Task, I've the option to use inheritance on the Artifact class to create CaseArtifact and TaskArtifact derived classes. The only difference between the two classes, however, would be the presence of a Case field or a Task field. In the database of course, I would have a single table, Artifact, with a type discriminator field and the CaseId and TaskId fields.

My question: is this a valid approach to solving this problem, or would creating a join table for each association (2 new tables, total) be a better approach?

+2  A: 

I would probably go with two tables - it makes the referential integrity-PK/FKs a little simpler to handle in the database, since you won't have to have a complex constraint based on the selector column.

(to reply to your comment - I ran out of space so post here as an edit) My overall philosophy is that the database should be modelled with database best practices (protect your perimeter and ensure database consistency, using as much RI and constraints as possible, provide all access through SPs, log activity as necessary, control all modes of access, use triggers where necessary) and the object model should be modelled with OOP best practices to provide a powerful and consistent API. It's the job of your SPs/data-access layer to handle the impedance mismatch.

If you just persist a well-designed object model to a database, your database won't have much intrinsic value (difficult to data mine, report, warehouse, metadata vague, etc) when viewed without going through the lens of the object model - this is fine for some application, typically not for mine.

If you just mimic a well-designed database structure in your application, without providing a rich OO API, your application will be difficult to maintain and the internal strucutres will be awkward to deal with - typically very procedural, rigid and with a lot of code duplication.

Cade Roux
so you think that the added complexity of having two additional join tables is less than the complexity of using STI? Since I'm using L2S, that would mean I'd have an EntitySet<JoinTable> in my classes, instead of a direct reference, right?
Josh E
See my edit. I typically have an OO model which does not mimic the database. Without knowing enough, I would probably have either Artifact.Case or Artifact.Task be valid (XOR) or have two classes CaseArtifact with .Case and TaskArtifact with .Task and Case.Artifacts be a collection of all the Artifacts associated with the Case and Task.Artifacts be a collection of all the Artifacts associated with the Task.
Cade Roux
You bring up some very good points in your edit. Providing a rich API is one of the central goals of my design. Releasing some time before Duke Nukem Forever is also important though. I've decided that I'm going to have to make some compromises to my domain model in order to 'make it work' with L2S. The resulting domain model is closer than to the DB schema than I particularly care for, but hopefully still OO enough to expose a rich API. I went with join tables as suggested, putting them in a different folder of the project to help separate them from 'real' domain objects.
Josh E
Good moves. I always iteratively build from every end - UI needs, model needs and DB needs. I'm always wary of catering too much to a particular tool - e.g. an ORM or LINQ2SQL or whatever - they are all leaky abstractions and will ultimately break down - hopefully they will always break down far beyond your requirements, but keeping an eye on that is always important, because the more time you have when you see the train coming, the better you can manage your product roadmap.
Cade Roux
Precisely. My current project doesn't really allow the kind of iterative building that normally would happen, but that's what programmatic integration tests are for! It's awesome being able to test my OR mappings as I write them, and know exactly what needs to be fixed up when I change the schema
Josh E
+1  A: 

I would consider finding commonalities in between case and task, for the lack of better word let's call it "CaseTask" and then sub-typing (inheriting) from that one. After that you attach document to the super-type.

UPDATE (after comment):
I would then consider something like this. Each document can be attached to several cases or tasks.



alt text

Damir Sudarevic
The only real domain commonality between a case and a task is the fact that both can have documents ('artifacts') attached to them. In fact, tasks are indirectly associated with cases (through a collection class).
Josh E
So one case has many tasks? Could you explain relationship between cases and tasks?
Damir Sudarevic
sure. A case has a workflow associated with it. A workflow is a collection of tasks, along with some other properties. A workflow is associated to a case, but the individual tasks within the workflow are not, since they are contained in the workflow itself.
Josh E
The update to your post looks good, and is pretty close to what I had in mind given the feedback from yours and Cade's answers. Where it gets complicated is how to model that in my POCO classes and the resulting L2S mapping...
Josh E