Okay, I'm really stuck with this one. Basically we've got a legacy table (+ normalization tables) in our database from which true domain entities should be created without altering the table schema. The table looks like this:
# identity table
| Domain (CK) | Group (CK) | Name (CK) | Password |
|-------------|------------|-----------|----------|
| root | | | |
| root | group1 | | |
| | group1 | someuser | XXXXXX |
| | group2 | | |
| | | otheruser | XXXXXX |
(CK = composite key)
The old application enforced the following rules:
- If only
Domain
is set, the entity is a domain which can contain users and groups - If only
Group
is set, the entity is a group which can contain users - If
Group
andDomain
is set, the entity is a group within a domain - If
Name
is set, the entity is a user within the respective group or domain (depending on which ones are set) - etc. (I hope you get the picture).
What we would like to have in the end would be entities like this (pseudo):
class Domain {
string Name;
addUser(user);
addGroup(group);
}
class Group {
string Name;
addUser(user);
}
class User {
string Name;
string Password;
}
The only two ways I can think of to resolve this would be either:
Creating a base class of all three of the identity classes and creating a second mapping table containing a discriminator. But then: How could I tell EF that the discriminator is in a different table? And how could I enforce the business rules that e.g. an entity within that table having both a
Group
and aName
is aUser
?Creating repositories to do the whole job with mapping and stuff and denoting every business rule as pure application logic, but then there would be no way to incorporate EFs cool stuff like lazy loading, automatic mapping, LINQ etc. and we could basically continue using our legacy code. ;)
Specific questions:
- Is it possible to map a discriminator to a different table in EF?
- Is it possible to make that discriminator not a value, but rather conditional? (E.g. discriminator for groups =>
hasValue(Group) && !hasValue(Name)
It is possible to cheat a bit and not consider these as full blown domain entities. Instead you can treat user management as a separate subdomain with its own bounded context. Isolate the BC from the rest of your domain with an anticorruption layer that translates from the language of this BC to the language of the general domain. Typically, the AC layer will involve mappers from your old, badly designed classes to new
Domain
,Group
andUser
classes.I think this is the most sensible way to start benefiting from clean user management entities in your main domain straight away, while keeping options open for a complete makeover of your identity module and database (which should be I guess the ultimate goal).