I received a great question from a reader recently. It’s about combining the domain model with an external library (edited for clarity):
Hi Vladimir! I’m a big fan of your posts and courses. I have a design question, can you help me?
I work on a project that uses AspNetCore.Identity for authentication. That library has an
IdentityUser
entity, which I use like this:AppUser : IdentityUserBut a question arises… If I want another entity to have an
Owner
property (of typeAppUser
), this makes my Domain project reference the AspNetCore.Identity package, coupling it with the infrastructure. This is bad, right?Can you help me with this dilemma? Thanks!!
I’ve written a lot about separating domain model from non-domain related concerns (I call this practice domain model isolation).
There two main reasons for such an isolation:
-
Enabling domain model testability — A domain model that talks to out-of-process dependencies becomes harder to test because you have to resort to mocks. You are also running into the risk of overusing those mocks and coupling your tests to implementation details.
-
Offloading as much of irrelevant responsibilities as possible from the domain model — Your business logic is complex enough already, you should take any opportunity to shave as much of that complexity off of it as possible.
The separation between the business logic and non-domain related concerns is often violated by the use of third party libraries (such as an ORM) directly in the domain layer.
However, the act of referencing a third party library isn’t bad in and on itself. After all, your domain model uses integers and strings from the core library, and technically, that library is external to your domain model too. Still, that wouldn’t be a violation of the domain model isolation. So, what gives?
The heuristic of referencing a third party library from the domain model is a good one, but it’s just that — a heuristic. It’s a proxy to the following two problems:
-
Referencing an out-of-process dependency. If the package used by the domain model is an out-of-process dependency (i.e. it’s a proxy for data that is not yet in the memory), then your domain model isn’t properly isolated. Always separate business logic from communication with out-of-process dependencies.
An example would be making an HTTP call to an external identity provider to validate the user. As far as I understand,
IdentityUser
doesn’t do that, so it’s not an issue in this particular case. -
Exposing functionality not relevant to your application. This is a problem because such functionality pollutes the domain model and may also lead to a breach in encapsulation.
For example, does
IdentityUser
expose aClaims
collection with JWT claims ASP.NET parsed from the access token? (I’m just speculating, I don’t know if it does.)If so, that’s an example of the functionality irrelevant to your domain model. Your
AppUser
probably should contain anEmail
property (which may work on top of theClaims
collection internally) but that collection itself is an implementation detail. And ifIdentityUser
allows for modification of those claims, that would be an example of breach in encapsulation because you most likely want to impose your own business rules on how the user email can be changed.
So, again, referencing an external package from the domain model is not a problem in and of itself, it’s these two issues that often emerge as a result of that.
Assuming that this IdentityUser
behaves as I described above (it doesn’t call out-of-process services, and it exposes functionality irrelevant to your domain model), what should you do?
Create a POCO User
class — class that doesn’t inherit from any built-in, complex framework classes.
If you have to use IdentityUser
because, say, ASP.NET automatically creates an instance of it as part of all incoming requests, then map that class onto your custom User
as soon as those requests hit your controllers. Your domain model should only work with User
, not IdentityUser
.
Remember, the domain model is the most important part of your project. Protect it from dilution. Eliminate the irrelevant and amplify the essentials.
--Vlad
https://enterprisecraftsmanship.com
Enjoy this message? Here are more things you might like:
Workshops — I offer a 2-day workshop for organizations on Domain-Driven Design and Unit Testing. Reply to this email to discuss.
Unit Testing Principles, Patterns and Practices — A book for people who already have some experience with unit testing and want to bring their skills to the next level.
Learn more »
My Pluralsight courses — The topics include Unit Testing, Domain-Driven Design, and more.
Learn more »