Primitive Obsession and Strongly Typed IDs in DDD w/ .NET 8 Microservices

Mehmet Ozkaya
4 min readMar 26, 2024

--

We are going to learn Primitive Obsession and Strongly Typed IDs in DDD with .NET 8 Microservices.

I have just published course — .NET 8 Microservices: C# 12, DDD, CQRS, Vertical/Clean Architecture.

🔍 Understanding Primitive Obsession

Primitive Obsession is a code smell where primitives (like string, int, Guid) are used for domain concepts, leading to ambiguity and errors. Primitive Obsession is akin to using a one-size-fits-all approach for domain concepts, where generic primitives (like int, string, Guid) are ubiquitously used. This over-reliance can blur the distinction between different domain concepts, leading to mix-ups and errors.

👉 For example, an orderId, customerId, and productId might all be represented as Guid or string, making it perilously easy to interchange them unintentionally.

🛠️ The Cure: Strongly Typed IDs

Strongly Typed IDs are the antidote to Primitive Obsession. Strong Typed IDs involve creating distinct types for each kind of ID in your domain. By defining distinct types for each ID in your domain, your code becomes more self-documenting and robust against mix-ups. This makes your code more expressive and less error-prone.

It clarifies which type of ID is expected and prevents accidentally using one type of ID (like a productId) where another (like an orderId) is intended.

🎯 Applying Strongly Typed IDs

Let’s walk through applying this pattern to an OrderItem class within our DDD context.

👀 Before the transition, our OrderItem might look something like this:

public class OrderItem : Entity<Guid>
{
public OrderItem(Guid orderId, Guid productId, int quantity, decimal price)
{
// Initialization logic
}
}

Here, the use of Guid for both orderId and productId is a classic example of Primitive Obsession, ripe for mix-ups.

🔄 After introducing Strongly Typed IDs, we first define our ID classes:

public record OrderId(Guid Value);
public record ProductId(Guid Value);

And then refactor our OrderItem to utilize these new types:

public class OrderItem : Entity<OrderItemId>
{
public OrderItem(OrderId orderId, ProductId productId, int quantity, decimal price)
{
// Enhanced initialization logic
}
}

💡 Benefits Unleashed

  • Type Safety: Incorrectly passing a ProductId where an OrderId is expected now triggers a compile-time error, safeguarding against subtle bugs.
  • Enhanced Readability: The constructor parameters explicitly state the expected ID type, making the code more intuitive.
  • IDE-Friendly: These distinct types are easily recognizable by IDEs, which aids in efficient refactoring and code navigation.

🎨 The Creation of Value Objects

In the Value Objects, OrderId, ProductId, and OrderItemId emerge, each a testament to our commitment to a domain model that speaks the language of clarity and distinction.

For example, OrderId.cs:

public record OrderId(Guid Value);

🌟 Key Takeaway: These value objects are not mere types; they are the very foundation upon which our domain’s integrity rests.

🛠️ Harmonizing Entity Base Class Types

Our final act of refinement ensures that every entity, from Order to OrderItem, embraces the Strongly Typed IDs, heralding an era where type safety is not just practiced but celebrated.

For instance, Order.cs evolves:

public class Order : Aggregate<OrderId> { ... }

🌟 Key Takeaway: This meticulous update across all entities fortifies our domain model, making it a bastion against the common foes of ambiguity and error.

📚 Diving into Ordering Microservices with DDD & Clean Architecture

Our journey navigates through the core essence and periphery of the Ordering system, revealing the blueprint of a system designed for resilience, flexibility, and domain-centricity.

🌟 Conclusion: Elevating Your Domain Model

Strongly Typed IDs are a straightforward yet potent remedy for Primitive Obsession in DDD. By endowing IDs with explicit types, your domain model gains in expressiveness, robustness, and maintainability, setting a solid foundation for a scalable and error-resistant system. Embrace Strongly Typed IDs and watch your domain model transform into a bastion of clarity and type safety.

I have just published course — .NET 8 Microservices: C# 12, DDD, CQRS, Vertical/Clean Architecture.

This is step-by-step development of reference microservices architecture that include microservices on .NET platforms which used ASP.NET Web API, Docker, RabbitMQ, MassTransit, Grpc, Yarp API Gateway, PostgreSQL, Redis, SQLite, SqlServer, Marten, Entity Framework Core, CQRS, MediatR, DDD, Vertical and Clean Architecture implementation with using latest features of .NET 8 and C# 12.

--

--

Mehmet Ozkaya

Software Architect | Udemy Instructor | AWS Community Builder | Cloud-Native and Serverless Event-driven Microservices https://github.com/mehmetozkaya