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 : IdentityUser

But a question arises…​ If I want another entity to have an Owner property (of type AppUser), 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 a Claims 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 an Email property (which may work on top of the Claims collection internally) but that collection itself is an implementation detail. And if IdentityUser 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:

Consulting and training — I offer consulting and group training for organizations on Unit Testing and Domain-Driven Design. Reply to this email with the word "consult" 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 »