Skip to main content

Creating Domain Entities

Domain entities are the core building blocks of your application. This guide walks you through creating entities that inherit from DomainEntity with proper validation and encapsulation.

Overview

Every domain entity in this architecture must:
  • Inherit from DomainEntity<TKey, TValidator>
  • Have private setters to protect domain invariants
  • Include a validator that inherits from EntityValidator<TEntity>
  • Use methods to modify properties with validation logic

Step-by-Step Guide

1

Create the Entity Validator

First, create a validator class using FluentValidation. This defines your business rules.Create a new file in Domain/Validators/YourEntityValidator.cs:
using Core.Domain.Validators;
using Domain.Constants;
using Domain.Entities;
using FluentValidation;

namespace Domain.Validators
{
    public class YourEntityValidator : EntityValidator<YourEntity>
    {
        public YourEntityValidator()
        {
            // Define business rules here
            RuleFor(x => x.PropertyOne)
                .NotNull()
                .NotEmpty()
                .WithMessage("PropertyOne cannot be null or empty");
            
            RuleFor(x => x.PropertyTwo)
                .GreaterThan(0)
                .WithMessage("PropertyTwo must be greater than zero");
        }
    }
}
Example from codebase: Domain/Validators/DummyEntityValidator.cs:13-20
2

Create the Domain Entity

Create your entity class that inherits from DomainEntity<TKey, TValidator>.Create a new file in Domain/Entities/YourEntity.cs:
using Core.Domain.Entities;
using Domain.Validators;

namespace Domain.Entities
{
    public class YourEntity : DomainEntity<string, YourEntityValidator>
    {
        // Properties with private setters to protect invariants
        public string PropertyOne { get; private set; }
        public int PropertyTwo { get; private set; }

        // Parameterless constructor for EF Core
        public YourEntity()
        {
        }

        // Constructor for creating new entities
        public YourEntity(string propertyOne, int propertyTwo)
        {
            Id = Guid.NewGuid().ToString();
            SetPropertyOne(propertyOne);
            PropertyTwo = propertyTwo;
        }

        // Constructor with explicit ID (for updates)
        public YourEntity(string id, string propertyOne, int propertyTwo)
        {
            Id = id;
            SetPropertyOne(propertyOne);
            PropertyTwo = propertyTwo;
        }

        // Methods to modify properties with validation
        public void SetPropertyOne(string value)
        {
            PropertyOne = value ?? throw new ArgumentNullException(nameof(value));
        }

        public void SetPropertyTwo(int value)
        {
            if (value <= 0)
                throw new ArgumentException("Value must be greater than zero");
            PropertyTwo = value;
        }
    }
}
Example from codebase: Domain/Entities/DummyEntity.cs:12-48
3

Understanding DomainEntity Base Class

The DomainEntity<TKey, TValidator> base class provides:
  • Id property: Generic key of type TKey
  • IsValid property: Returns true if all validation rules pass
  • GetErrors() method: Returns a list of validation errors
  • Automatic validation: Runs FluentValidation rules
// Check if entity is valid
if (!entity.IsValid)
{
    var errors = entity.GetErrors();
    // Handle validation errors
}
Reference: Core.Domain/Entities/DomainEntity.cs:13-45
4

Use Value Objects for Complex Properties

For properties that represent value objects or enums, define them separately:
// In Domain/Enums/Enums.cs
namespace Domain.Enums
{
    public class Enums
    {
        public enum Status
        {
            Active,
            Inactive,
            Pending
        }
    }
}
Then use them in your entity:
using static Domain.Enums.Enums;

public class YourEntity : DomainEntity<string, YourEntityValidator>
{
    public Status CurrentStatus { get; private set; }

    public void SetStatus(Status status)
    {
        CurrentStatus = status;
    }
}
Example from codebase: Domain/Entities/DummyEntity.cs:19
5

Implement Domain Logic

Add business logic methods to your entity:
public class YourEntity : DomainEntity<string, YourEntityValidator>
{
    public DateTime CreatedAt { get; private set; }
    public DateTime? UpdatedAt { get; private set; }

    public void MarkAsUpdated()
    {
        UpdatedAt = DateTime.UtcNow;
    }

    public bool IsExpired(TimeSpan expirationPeriod)
    {
        return DateTime.UtcNow - CreatedAt > expirationPeriod;
    }
}

Key Principles

Private setters enforce encapsulation by ensuring properties can only be modified through entity methods. This allows you to:
  • Validate changes before applying them
  • Maintain business invariants
  • Track modifications and raise domain events
  • Prevent invalid state transitions
Validation happens automatically when:
  • You check the IsValid property
  • You call GetErrors() method
  • The entity is saved (validated in the use case handler)
Best practice: Always validate before persisting:
if (!entity.IsValid)
    throw new InvalidEntityDataException(entity.GetErrors());
You have two options:
  • GUID: Id = Guid.NewGuid().ToString() (recommended for distributed systems)
  • Database-generated: Let the database assign the ID
  • Custom: Implement your own ID generation logic
The example uses GUID for immediate ID availability.

Complete Example

Here’s the complete DummyEntity implementation from the codebase:
using Core.Domain.Entities;
using Domain.Validators;
using static Domain.Enums.Enums;

namespace Domain.Entities
{
    public class DummyEntity : DomainEntity<string, DummyEntityValidator>
    {
        public string DummyPropertyOne { get; private set; }
        public DummyValues DummyPropertyTwo { get; private set; }

        public DummyEntity()
        {
        }

        public DummyEntity(string dummyPropertyOne, DummyValues dummyPropertyTwo)
        {
            Id = Guid.NewGuid().ToString();
            SetdummyPropertyOne(dummyPropertyOne);
            DummyPropertyTwo = dummyPropertyTwo;
        }

        public DummyEntity(string dummyIdProperty, string dummyPropertyOne, DummyValues dummyPropertyTwo)
        {
            Id = dummyIdProperty;
            SetdummyPropertyOne(dummyPropertyOne);
            DummyPropertyTwo = dummyPropertyTwo;
        }

        public void SetdummyPropertyOne(string value)
        {
            DummyPropertyOne = value ?? throw new ArgumentNullException(nameof(value));
        }

        public void SetdummyPropertyTwo(DummyValues value)
        {
            DummyPropertyTwo = value;
        }
    }
}
Reference: Domain/Entities/DummyEntity.cs:12-48

Next Steps

Implementing Use Cases

Learn how to create commands and queries that work with your entities

Database Setup

Configure Entity Framework to persist your entities

Build docs developers (and LLMs) love