Skip to content

AlexDenton/Optional

Repository files navigation

Optional

Proof of concept for partially materialized domain entities

Disclaimer: This is a proof of concept and not production-ready.

Background

This project is a potential solution to a problem that arises when trying to model objects from business requirements. In particular, the problem I am trying to solve arises in trying to follow some of the rules of Domain-Driven Design. Namely, the rule that all domain entities should be fully materialized at the domain layer. This rule causes a tension between the desire to model your entities the way you reason about them in your business model and performance.

For many people this is not a problem because they use some sort of ORM that allows them to lazily retrieve values. However, for many reasons ORMs are not always an option. Sometimes you simply can't model your persistence model with your ORM, sometimes the queries generated by the ORM are not performant enough, and sometimes you have a polyglot persistence model which doesn’t lend itself to an ORM.

If you don't/can't use an ORM you are faced with a choice:

  1. Eagerly fetching all data regardless of whether you need it
  2. Partially materializing your domain entities
  3. Arbitrarily remodeling your domain entities for performance rather than business reasons

For me Option 1 is an impractical answer and Option 3 is a disatisfying one. It is Option 2 that this project is intended to explore.

The real problem with partially materializing your domain entities is ambiguity. Someone consuming the entities has no idea whether a value is null because the value is actually null or if the entity simply isn't fully materialized. This is where the Optional type comes into play.

What is it?

At its core Optional is a very simple idea that is very similar to the Nullable type with some small but important distinctions. A Nullable is a type that has two properties: HasValue and Value. HasValue simply returns false if the value is null and true otherwise. Value returns the value if it isn't null and throws an exception if it is. Again, this is very close to what we want but with one small problem: there's no way to distinguish between a property that's been set to null and one that hasn't been set. Optional also provides two properties: IsSet and Value. IsSet returns true if the value has been set at any time regardless of whether it was set to null. Value returns the value if it has been set and throws an exception otherwise.

With this type someone can look at a partially materialized domain entity and know unambiguously whether a value is null or whether it simply hasn't been set yet. It also protects the consumer from using Value when they shouldn't by throwing an exception.

Examples

For examples please see my unit tests here: https://github.com/AlexDenton/Optional/blob/master/Optional.Tests/OptionalTests.cs

Implications for Serialization

This also has exciting implications for serialization. When making DTOs to return to some client you are often faced with a similar choice as above:

  1. A bunch of little DTOs for each different context
  2. One big DTO with all the properties you could ever want

Option 1 is okay but means writing a lot of DTOs and a lot of boilerplate mapping code. Option 2 is also okay but loses the semantic meaning of your DTOs during serialization. To elaborate on that let's say you want to serialize a property and its value is null. Well, you can either tell the serializer to ignore null values or you can tell it to keep them. Unfortunately it's all or nothing and you could be losing semantic meaning either way. Either you're returning data that isn't true (that a value is null when it really isn't), or you lose the ability to tell the client that a value actually is null.

With the Optional type you're able to strike a middle ground. By making a custom ContractResolver and JsonConverter you're able to ignore a value if it isn't set and show a value if it is (even if it was set to a null value). There will still be times when it is appropriate to create a new DTO entirely but using Optional gives you the option of not creating a new DTO every time.

Examples

For examples please see my unit tests here: https://github.com/AlexDenton/Optional/blob/master/Optional.Tests/OptionalSerializationTests.cs

About

Proof of concept for partially materialized domain entities

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages