# Deployment and DevOps Standards for Domain Driven Design
This document outlines coding standards related to Deployment and DevOps specifically within the context of Domain Driven Design (DDD). It aims to provide clear guidance on building, deploying, and managing DDD applications effectively, while maintaining domain integrity and agility.
## 1. Build Processes and CI/CD for DDD
### 1.1 Standards
* **Do This:** Implement Continuous Integration (CI) and Continuous Delivery/Deployment (CD) pipelines. This automates building, testing, and deploying your application, reducing manual errors and accelerating delivery.
* **Don't Do This:** Rely on manual build and deployment processes. This is error-prone, time-consuming, and hinders agility.
* **Why:** CI/CD ensures frequent and reliable releases, early detection of integration issues, and faster feedback loops for developers.
### 1.2 Build Process Considerations
* **Standard:** Use build tools that support dependency management and reproducible builds (e.g., Maven/Gradle for Java, npm/yarn for JavaScript, NuGet for .NET).
* **Standard:** Ensure the build process includes static code analysis, unit tests, integration tests, and potentially performance tests.
* **Standard:** Version your artifacts (e.g., Docker images, JAR files) with semantic versioning, facilitating dependency management and rollback capabilities.
* **Why:** Repeatable builds are crucial for consistent deployment and easy rollback strategies. Unit tests within the build process guarantee quality, and static code analysis can find potential errors early.
### 1.3 CI Pipeline Examples
**Example (GitHub Actions):**
"""yaml
name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '17'
distribution: 'temurin'
- name: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Build with Gradle
run: ./gradlew build
- name: Run Static Analysis - SonarQube
run: ./gradlew sonarqube -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_TOKEN
- name: Docker Build & Push
if: github.ref == 'refs/heads/main'
run: |
docker login -u ${{ secrets.DOCKERHUB_USERNAME }} -p ${{ secrets.DOCKERHUB_TOKEN }}
docker build -t your-app:${GITHUB_SHA} .
docker tag your-app:${GITHUB_SHA} your-app:latest
docker push your-app:${GITHUB_SHA}
docker push your-app:latest
"""
**Example (Jenkins):**
Configure a pipeline job:
1. **Source Code Management:** Connect to your code repository (e.g. Git).
2. **Build Triggers:** Configure triggers like "Poll SCM" on pushes to the main branch.
3. **Build Steps**:
* Execute shell scripts (e.g., "gradle clean build", "docker build -t my-app .", "docker push my-app").
* Use Jenkins plugins for SonarQube or other static analysis tools.
### 1.4 CD Pipeline Examples
**Example (ArgoCD with Kubernetes):**
1. Create ArgoCD application definition yaml file:
"""yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-ddd-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/your-ddd-repo.git
targetRevision: HEAD
path: k8s/deploy
destination:
server: https://kubernetes.default.svc
namespace: my-namespace
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
"""
2. Directory Structure: Your Git repository should include a "/k8s/deploy" directory with your Kubernetes manifests (Deployment, Service, etc.).
**Example (Terraform deployment):**
Terraform can be used to provision infrastructure for your DDD application
"""terraform
resource "aws_instance" "app_server" {
ami = "ami-xxxxxxxxxxxxx" # Replace with your AMI ID
instance_type = "t2.medium"
key_name = "your_key"
vpc_security_group_ids = ["sg-xxxxxxxxxxxxx"]
tags = {
Name = "my-ddd-app-server"
}
user_data = <<-EOF
#!/bin/bash
# Install dependencies, pull image, run docker
sudo apt-get update
sudo apt-get install -y docker.io
sudo systemctl start docker
sudo systemctl enable docker
docker login -u your-dockerhub-username -p your-dockerhub-password # Store credentials securely
docker pull your-app:latest
docker run -d -p 80:8080 your-app:latest
EOF
}
"""
## 2. Production Considerations for DDD
### 2.1 Database Migrations
* **Do This:** Use database migration tools (e.g., Flyway, Liquibase, Entity Framework Migrations) to manage schema changes.
* **Don't Do This:** Apply schema changes manually.
* **Why:** Automating database migrations ensures consistent schema updates, reduces downtime, and supports version control of schema changes.
* **Standard:** Integrate database migrations into your CI/CD pipeline. Use idempotent migration scripts. These scripts should only apply changes that are not already applied ensuring deployments can be rerun without breaking.
**Example (Flyway):**
1. Create Flyway migration scripts in the "src/main/resources/db/migration" directory:
"V1__Create_Order_Table.sql"
"""sql
CREATE TABLE orders (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL,
order_date TIMESTAMP NOT NULL
);
"""
"V2__Add_Order_Status.sql"
"""sql
ALTER TABLE orders ADD COLUMN status VARCHAR(20) NOT NULL DEFAULT 'PENDING';
"""
2. Configure Flyway in your application configuration (e.g., "application.properties" if using Spring Boot):
"""properties
spring.flyway.url=jdbc:postgresql://localhost:5432/your_database
spring.flyway.user=your_user
spring.flyway.password=your_password
"""
### 2.2 Infrastructure as Code (IaC)
* **Do This:** Use IaC tools like Terraform, CloudFormation, or Azure Resource Manager to provision and manage infrastructure resources.
* **Don't Do This:** Manually configure infrastructure resources.
* **Why:** IaC provides a declarative way to define infrastructure, ensures consistent environments, and facilitates automation and version control.
**Example (Terraform - AWS):**
"""terraform
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
tags = {
Name = "main-vpc"
}
}
resource "aws_subnet" "public_subnet" {
vpc_id = aws_vpc.main.id
cidr_block = "10.0.1.0/24"
availability_zone = "us-east-1a"
tags = {
Name = "public-subnet"
}
}
"""
### 2.3 Monitoring and Logging
* **Do This:** Implement comprehensive monitoring and logging for your application. Use structured logging and integrate with centralized logging systems (e.g., ELK stack, Splunk, Datadog).
* **Don't Do This:** Rely on ad-hoc logging or manual monitoring.
* **Why:** Monitoring and logging provide insights into application performance, identify issues early, and facilitate troubleshooting.
* **Standard:** Monitor key DDD metrics such as aggregate processing time, event handling latency, and command execution successes/failures.
**Example (Structured Logging using Logback with Spring Boot):**
1. Add Logback dependency (it's usually included by default in Spring Boot).
2. Configure a "logback-spring.xml" file:
"""xml
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
application.log
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
"""
3. Log events using a centralized logger:
"""java
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
private static final Logger logger = LoggerFactory.getLogger(OrderService.class);
public void placeOrder(Order order) {
logger.info("Placing order with ID: {}", order.getId());
// ... Order placement logic ...
logger.debug("Order {} : {} placed successfully!", order.getId(), order.getDescription());
if (order.getTotalAmount() > 1000) {
logger.warn("Large order placed with ID: {} and amount: {}", order.getId(), order.getTotalAmount());
}
try {
// Simulate an exception
throw new RuntimeException("Simulated error");
} catch (RuntimeException e) {
logger.error("Error processing order with ID: {}", order.getId(), e);
}
}
}
"""
### 2.4 Security Best Practices
* **Do This:** Implement security best practices throughout the entire application lifecycle. This includes authentication, authorization, encryption, vulnerability scanning, and penetration testing.
* **Don't Do This:** Neglect security considerations or rely on default security configurations.
* **Why:** Ensuring security is crucial to protect sensitive data, prevent unauthorized access, and maintain application integrity.
* **Standard:** Follow the principle of least privilege. Grant only the necessary permissions to users and services.
* **Standard:** Regularly update dependencies to address known vulnerabilities.
* **Standard:** Utilize tools like OWASP ZAP or SonarQube for static and dynamic security analysis.
* **Standard:** Encrypt sensitive data both in transit and at rest. Use HTTPS for all communication.
**Example (Spring Security):**
"""java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authz) -> authz
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasRole("USER")
.anyRequest().authenticated()
)
.httpBasic(withDefaults()); // Basic Authentication
return http.build();
}
@Bean
public UserDetailsService userDetailsService() {
UserDetails user =
User.withDefaultPasswordEncoder()
.username("user")
.password("password")
.roles("USER")
.build();
UserDetails admin =
User.withDefaultPasswordEncoder()
.username("admin")
.password("admin")
.roles("ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}
"""
### 2.5 Technology-Specific Details
* **Cloud Platforms:** Leverage cloud-native services like AWS Lambda, Azure Functions, or Google Cloud Functions for serverless components within your DDD application.
* **Containerization:** Always containerize your DDD application using Docker. This provides consistency across environments. Optimize Docker images for size and security. Use multi-stage builds to reduce final image size and exclude build dependencies.
* **Orchestration:** Use Kubernetes or Docker Swarm for container orchestration. This ensures scalability, resilience, and automated deployment.
* **Message Queues:** Utilize message queues like RabbitMQ or Kafka for asynchronous communication between bounded contexts. Configure proper message durability and retry mechanisms.
## 3. Domain Driven Design Specific Considerations for Deployment
### 3.1 Bounded Context Deployment
* **Standard:** Deploy each bounded context as an independent service (microservice architecture is well-suited).
* **Why:** This aligns with DDD principles, promoting autonomy, independent evolution, and reduced coupling between domain areas.
* **Deployment Strategy:** Each bounded context should have its own CI/CD pipeline, infrastructure, and database. This enables independent scaling and release cycles.
* **Inter-service Communication:** Choose appropriate inter-service communication patterns such as:
* **RESTful APIs:** Simple and widely understood.
* **Message Queues (e.g., RabbitMQ, Kafka):** Asynchronous and loosely coupled. Essential for eventual consistency.
* **gRPC:** High-performance, contract-based communication, especially suited for internal services.
### 3.2 Eventual Consistency
* **Standard:** Embrace eventual consistency when propagating data changes between bounded contexts. Use domain events and message queues to ensure reliable delivery.
* **Standard:** Implement idempotent event handlers to avoid duplicate processing. Ensure handling logic is unaffected by processing the same message multiple times.
* **Standard:** Monitor message queue health and latency to detect and resolve issues promptly. Implement dead-letter queues to handle undeliverable messages.
**Example (Event-Driven deployment using Kafka):**
1. Order Service publishes "OrderCreatedEvent" to Kafka
2. Shipping Service subscribes to "OrderCreatedEvent" and creates shipment.
"""java
// Order Service
@Service
public class OrderService {
private final KafkaTemplate kafkaTemplate;
@Autowired
public OrderService(KafkaTemplate kafkaTemplate) {
this.kafkaTemplate = kafkaTemplate;
}
public void createOrder(Order order) {
// ... order creation logic
OrderCreatedEvent event = new OrderCreatedEvent(order.getId(), order.getCustomerId());
kafkaTemplate.send("order-created-topic", event);
}
}
// Shipping Service
@Service
@KafkaListener(topics = "order-created-topic", groupId = "shipping-group")
public class ShippingService {
public void handleOrderCreatedEvent(OrderCreatedEvent event) {
// ... create shipment
System.out.println("Received OrderCreatedEvent: " + event.getOrderId());
}
}
"""
### 3.3 Versioning Strategies
* **Standard:** Use API versioning to manage changes to public APIs. This allows clients to upgrade at their own pace. Consider both URI versioning ("/v1/orders") and header-based versioning ("Accept: application/vnd.yourcompany.orders.v1+json").
* **Standard:** Implement data versioning for domain events. This helps ensure compatibility between event publishers and consumers over time. Include a version field in the event payload.
* **Blue/Green Deployments:** Consider blue/green deployments to minimize downtime during releases. Deploy the new version (green) alongside the existing version (blue). Switch traffic to the green environment after verifying the new version is working correctly.
## 4. Common Anti-Patterns and Mistakes
* **Shared Database Across Bounded Contexts:** This violates DDD principles and tightly couples the services. Each bounded context should have its own database or schema to ensure autonomy.
* **God Object/Service:** Avoid creating large, monolithic services that span multiple domains. Break down large services into smaller, more manageable bounded contexts.
* **Ignoring Domain Events:** Domain events are a crucial part of DDD. Failing to use them can lead to tight coupling and make it difficult to maintain eventual consistency.
* **Inadequate Testing:** Neglecting unit, integration, and end-to-end tests can lead to undetected bugs and deployment failures.
## 5. Performance Optimization Techniques
* **Caching:** Implement caching strategies at various levels (e.g., HTTP caching, in-memory caching, database caching) to reduce latency and improve performance. Use tools like Redis or Memcached for distributed caching.
* **Database Optimization:** Optimize database queries, use indexes appropriately, and consider database sharding or partitioning for large datasets.
* **Asynchronous Processing:** Use message queues for long-running or resource-intensive operations to avoid blocking the user interface or other services.
* **Load Balancing:** Distribute traffic across multiple instances of your services using load balancers to ensure high availability and scalability.
## 6. Security Best Practices specific to DDD
* **Authentication and Authorization:** Implement robust authentication and authorization mechanisms. Use industry-standard protocols like OAuth 2.0 or OpenID Connect.
* **Input Validation:** Thoroughly validate all input data to prevent injection attacks (e.g., SQL injection, cross-site scripting).
* **Data Encryption:** Encrypt sensitive data both in transit and at rest. Use HTTPS for all communication.
* **Secure Configuration Management:** Store sensitive configuration data (e.g., passwords, API keys) securely using secrets management tools like HashiCorp Vault or AWS Secrets Manager.
* **Regular Security Audits:** Perform regular security audits and penetration testing to identify and address potential vulnerabilities.
danielsogl
Created Mar 6, 2025
This guide explains how to effectively use .clinerules
with Cline, the AI-powered coding assistant.
The .clinerules
file is a powerful configuration file that helps Cline understand your project's requirements, coding standards, and constraints. When placed in your project's root directory, it automatically guides Cline's behavior and ensures consistency across your codebase.
Place the .clinerules
file in your project's root directory. Cline automatically detects and follows these rules for all files within the project.
# Project Overview project: name: 'Your Project Name' description: 'Brief project description' stack: - technology: 'Framework/Language' version: 'X.Y.Z' - technology: 'Database' version: 'X.Y.Z'
# Code Standards standards: style: - 'Use consistent indentation (2 spaces)' - 'Follow language-specific naming conventions' documentation: - 'Include JSDoc comments for all functions' - 'Maintain up-to-date README files' testing: - 'Write unit tests for all new features' - 'Maintain minimum 80% code coverage'
# Security Guidelines security: authentication: - 'Implement proper token validation' - 'Use environment variables for secrets' dataProtection: - 'Sanitize all user inputs' - 'Implement proper error handling'
Be Specific
Maintain Organization
Regular Updates
# Common Patterns Example patterns: components: - pattern: 'Use functional components by default' - pattern: 'Implement error boundaries for component trees' stateManagement: - pattern: 'Use React Query for server state' - pattern: 'Implement proper loading states'
Commit the Rules
.clinerules
in version controlTeam Collaboration
Rules Not Being Applied
Conflicting Rules
Performance Considerations
# Basic .clinerules Example project: name: 'Web Application' type: 'Next.js Frontend' standards: - 'Use TypeScript for all new code' - 'Follow React best practices' - 'Implement proper error handling' testing: unit: - 'Jest for unit tests' - 'React Testing Library for components' e2e: - 'Cypress for end-to-end testing' documentation: required: - 'README.md in each major directory' - 'JSDoc comments for public APIs' - 'Changelog updates for all changes'
# Advanced .clinerules Example project: name: 'Enterprise Application' compliance: - 'GDPR requirements' - 'WCAG 2.1 AA accessibility' architecture: patterns: - 'Clean Architecture principles' - 'Domain-Driven Design concepts' security: requirements: - 'OAuth 2.0 authentication' - 'Rate limiting on all APIs' - 'Input validation with Zod'
# Component Design Standards for Domain Driven Design This document outlines the coding standards for component design within a Domain-Driven Design (DDD) context. It aims to provide clear guidance for developers building robust, maintainable, and scalable software systems aligned with DDD principles. These standards will focus on creating loosely coupled, highly cohesive components that accurately reflect the business domain. ## 1. Introduction to Component Design in DDD Component design in DDD involves breaking down a large system into smaller, manageable, and independently deployable units. Each component ideally represents a bounded context or a well-defined part of it. This approach promotes modularity, reusability, and easier maintenance. Good component design isolates domain logic and reduces the complexity of the overall system. **Key Principles:** * **High Cohesion:** Each component should have a single, well-defined responsibility. All elements within the component should be closely related and contribute to that responsibility. * **Loose Coupling:** Components should minimize dependencies on each other. Changes in one component should ideally have minimal impact on other components. * **Explicit Interfaces:** Components should communicate through well-defined interfaces that clearly outline their functionality. These interfaces should be stable and versioned. * **Bounded Context Alignment:** Components should closely mirror the bounded contexts identified during domain analysis. Each component should be an autonomous unit that encapsulates a specific part of the domain. ## 2. Defining Components and Their Boundaries ### 2.1. Bounded Context Mapping **Standard:** Components must align directly with bounded contexts. Each bounded context should ideally be represented by one or more components. * **Do This:** Explicitly map components to bounded contexts during the design phase. Document the rationale behind each component's boundaries. * **Don't Do This:** Allow components to span multiple bounded contexts, blurring the lines of responsibility. **Why:** Ensures clear separation of concerns and prevents domain model contamination. **Example:** Let's say we have an e-commerce system with two bounded contexts: "Catalog" and "OrderManagement". Each context would be represented by separate components. """csharp // Catalog Component namespace ECommerce.Catalog { public class Product { public Guid ProductId { get; set; } public string Name { get; set; } public decimal Price { get; set; } } public interface IProductRepository { Product GetProduct(Guid productId); void SaveProduct(Product product); } } // OrderManagement Component namespace ECommerce.OrderManagement { public class Order { public Guid OrderId { get; set; } public Guid CustomerId { get; set; } public List<OrderItem> Items { get; set; } = new List<OrderItem>(); } public class OrderItem { public Guid ProductId { get; set; } public int Quantity { get; set; } public decimal Price { get; set; } } public interface IOrderRepository { Order GetOrder(Guid orderId); void SaveOrder(Order order); } } """ ### 2.2. Component Granularity **Standard:** Components should be sized appropriately, balancing cohesion and reusability. Avoid overly large or overly granular components. * **Do This:** Aim for components that are small enough to understand and maintain easily but large enough to represent a cohesive unit of functionality. Consider Conway's Law: your software architecture will likely mirror your team structure. * **Don't Do This:** Create monolithic components that contain too much unrelated functionality or excessively small components that lead to unnecessary complexity. **Why:** Improves maintainability and reduces the risk of ripple effects from changes. **Example:** In an Identity and Access Management (IAM) system, it might be beneficial to split the component into "Authentication" and "Authorization" components. Overly granular architecture might have components like "UserRegistrationComponent", "PasswordResetComponent", which makes collaboration and data access very difficult. ### 2.3. Explicit Dependencies **Standard:** All dependencies between components must be declared explicitly. Use dependency injection or other mechanisms to manage dependencies. * **Do This:** Favor constructor injection to clearly define a component's dependencies. * **Don't Do This:** Rely on implicit or hidden dependencies that are not easily discoverable. **Why:** Enhances testability and allows for better control over component interactions. **Example:** """csharp // Good: Constructor injection clearly defines dependencies public class OrderService { private readonly IOrderRepository _orderRepository; private readonly IProductRepository _productRepository; public OrderService(IOrderRepository orderRepository, IProductRepository productRepository) { _orderRepository = orderRepository; _productRepository = productRepository; } public void PlaceOrder(Guid orderId, Guid productId, int quantity) { var order = _orderRepository.GetOrder(orderId); var product = _productRepository.GetProduct(productId); // Process the order and save to repository ... } } // Bad: Relying on service location or static access public class BadOrderService { public void PlaceOrder(Guid orderId) { // Hard to test and understand dependencies IOrderRepository orderRepository = ServiceLocator.GetService<IOrderRepository>(); Order order = orderRepository.GetOrder(orderId); //... } } """ ## 3. Component Communication and Integration ### 3.1. Anti-Corruption Layer (ACL) **Standard:** Utilize an Anti-Corruption Layer when integrating with external systems or components that have incompatible models. * **Do This:** Create a translation layer that adapts the external system's data model to your domain model. This layer should isolate your domain from the external system's quirks and changes. * **Don't Do This:** Directly expose your domain model to external systems, leading to domain model corruption. **Why:** Protects the integrity of your domain model and prevents external dependencies from leaking into your core logic. **Example:** """csharp // External System Data Model public class ExternalCustomer { public string CustomerID { get; set; } public string FullName { get; set; } public string AddressLine1 { get; set; } public string City { get; set; } } // Domain Model namespace ECommerce.Customer { public class Customer { public Guid CustomerId { get; set; } public string Name { get; set; } public Address Address { get; set; } } public class Address { public string Street { get; set; } public string City { get; set; } } } // Anti-Corruption Layer public interface ICustomerAdapter { Customer GetCustomer(string externalCustomerId); } public class CustomerAdapter : ICustomerAdapter { private readonly IExternalCustomerService _externalCustomerService; public CustomerAdapter(IExternalCustomerService externalCustomerService) { _externalCustomerService = externalCustomerService; } public Customer GetCustomer(string externalCustomerId) { ExternalCustomer externalCustomer = _externalCustomerService.GetExternalCustomer(externalCustomerId); // Translate external data to domain model return new Customer { CustomerId = Guid.Parse(externalCustomer.CustomerID), Name = externalCustomer.FullName, Address = new Address { Street = externalCustomer.AddressLine1, City = externalCustomer.City } }; } } """ ### 3.2. Domain Events **Standard:** Use Domain Events to communicate state changes between components within a bounded context and across bounded contexts. * **Do This:** Publish domain events when significant state changes occur within a component. Subscribe to domain events to react to changes in other components. Implement eventual consistency where applicable. * **Don't Do This:** Directly access and modify the state of other components, creating tight coupling. Avoid relying on synchronous calls between components for critical operations. **Why:** Decouples components, promotes eventual consistency, and improves system resilience. **Example:** """csharp // Domain Event public class OrderCreatedEvent { public Guid OrderId { get; set; } public Guid CustomerId { get; set; } } // Component A (Order Management) public class OrderService { private readonly IOrderRepository _orderRepository; private readonly IDomainEventPublisher _domainEventPublisher; public OrderService(IOrderRepository orderRepository, IDomainEventPublisher domainEventPublisher) { _orderRepository = orderRepository; _domainEventPublisher = domainEventPublisher; } public void CreateOrder(Guid orderId, Guid customerId) { var order = new Order { OrderId = orderId, CustomerId = customerId }; _orderRepository.SaveOrder(order); var orderCreatedEvent = new OrderCreatedEvent { OrderId = orderId, CustomerId = customerId }; _domainEventPublisher.Publish(orderCreatedEvent); } } // Component B (Customer Service) public class CustomerService { public void Handle(OrderCreatedEvent orderCreatedEvent) { // Update customer information based on the new order Console.WriteLine($"New Order Created for Customer: {orderCreatedEvent.CustomerId}"); } } //Simplified event publisher public interface IDomainEventPublisher { void Publish<T>(T domainEvent); } """ ### 3.3. Asynchronous Communication **Standard:** Prefer asynchronous communication between components, especially across bounded contexts, to improve performance and availability. * **Do This:** Utilize message queues (e.g., RabbitMQ, Kafka) or other asynchronous mechanisms to decouple components. * **Don't Do This:** Rely heavily on synchronous REST API calls between components, leading to performance bottlenecks and increased risk of failure. **Why:** Improves system responsiveness and prevents failures in one component from cascading to others. ## 4. Component Design Patterns ### 4.1. Aggregate Root **Standard:** Each component should expose Aggregate Roots as its primary entry points. Treat Aggregates as consistency boundaries. * **Do This:** Design aggregates to encapsulate related entities and value objects. Expose methods on the aggregate root to perform operations that maintain the aggregate's consistency. * **Don't Do This:** Allow direct access to entities within an aggregate from outside the aggregate. **Why:** Enforces data integrity and consistency within the component. **Example:** """csharp // Aggregate Root: Order namespace ECommerce.OrderManagement { public class Order { public Guid OrderId { get; private set; } public Guid CustomerId { get; private set; } private readonly List<OrderItem> _items = new List<OrderItem>(); public IReadOnlyCollection<OrderItem> Items => _items.AsReadOnly(); public OrderStatus Status { get; private set; } // Private constructor for object creation within the Aggregate private Order(Guid orderId, Guid customerId) { OrderId = orderId; CustomerId = customerId; Status = OrderStatus.Created; } public static Order Create(Guid orderId, Guid customerId) { return new Order(orderId, customerId); } public void AddItem(Guid productId, int quantity, decimal price) { // Business rules and validations if(quantity <= 0) { throw new InvalidOperationException("Quantity must be positive."); } _items.Add(new OrderItem(productId, quantity, price)); } public void Submit() { //Business rules - only allow to submit orders once, only allow to submit orders with items Status = OrderStatus.Submitted; } } public enum OrderStatus { Created, Submitted, Cancelled, Shipped } public class OrderItem { public Guid ProductId { get; private set; } public int Quantity { get; private set; } public decimal Price { get; private set; } // Private constructor and no setters to enforce immutability after creation. internal OrderItem(Guid productId, int quantity, decimal price) { ProductId = productId; Quantity = quantity; Price = price; } } } """ ### 4.2. Factory Pattern **Standard:** Use factories to encapsulate the complex instantiation logic of aggregates and entities. * **Do This:** Create factory classes or methods to manage the creation of objects with intricate dependencies or initialization requirements. * **Don't Do This:** Directly instantiate complex objects throughout the codebase, scattering initialization logic. **Why:** Simplifies object creation, improves testability, and promotes code reuse. **Example:** """csharp // Factory for creating orders. namespace ECommerce.OrderManagement { public interface IOrderFactory { Order CreateOrder(Guid orderId, Guid customerId); } public class OrderFactory : IOrderFactory { private readonly IProductRepository _productRepository; public OrderFactory(IProductRepository productRepository) { _productRepository = productRepository; } public Order CreateOrder(Guid orderId, Guid customerId) { // Perform any necessary initialization or validation logic. // Retrieve default products or apply discounts. var order = Order.Create(orderId, customerId); // Add default items // order.AddItem(defaultProductId, 1, defaultProductPrice); return order; } } } """ ### 4.3 Repository Pattern **Standard:** Use the Repository pattern to abstract data access logic from the domain model. * **Do This:** Define interfaces for repositories that specify the data access operations required by the domain. Implement repositories using specific data access technologies (e.g., Entity Framework, NoSQL databases). * **Don't Do This:** Directly access data access technologies from the domain model, creating tight coupling and hindering testability. **Why:** Decouples the domain model from data access concerns, making it easier to change data access technologies without impacting the core domain logic. **Example:** """csharp // Repository Interface namespace ECommerce.OrderManagement { public interface IOrderRepository { Order GetOrder(Guid orderId); void SaveOrder(Order order); void DeleteOrder(Guid orderId); } // Entity Framework Implementation public class OrderRepository : IOrderRepository { private readonly OrderContext _context; public OrderRepository(OrderContext context) { _context = context; } public Order GetOrder(Guid orderId) { return _context.Orders.FirstOrDefault(o => o.OrderId == orderId); } public void SaveOrder(Order order) { _context.Orders.Update(order); _context.SaveChanges(); } public void DeleteOrder(Guid orderId) { var order = _context.Orders.FirstOrDefault(o => o.OrderId == orderId); if (order != null) { _context.Orders.Remove(order); _context.SaveChanges(); } } } } """ ## 5. Technology-Specific Considerations (C# Example) ### 5.1. Dependency Injection Container **Standard:** Utilize a Dependency Injection (DI) container (e.g., Microsoft.Extensions.DependencyInjection, Autofac) to manage component dependencies. * **Do This:** Register component dependencies with the DI container and use constructor injection to inject dependencies into components. * **Don't Do This:** Manually manage component dependencies or use service locator patterns, leading to tight coupling and reduced testability. **Why:** Simplifies dependency management, improves testability, and promotes loose coupling. **Example:** """csharp // Configure DI container using Microsoft.Extensions.DependencyInjection; public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddScoped<IOrderRepository, OrderRepository>(); services.AddScoped<IOrderFactory, OrderFactory>(); services.AddScoped<OrderService>(); //... configure other services } } // Usage in a controller: public class OrderController : ControllerBase { private readonly OrderService _orderService; public OrderController(OrderService orderService) { _orderService = orderService; } [HttpPost] public IActionResult CreateOrder(Guid orderId, Guid customerId){ _orderService.CreateOrder(orderId, customerId); return Ok(); } } """ ### 5.2. Asynchronous Programming (async/await) **Standard:** Use the "async" and "await" keywords for asynchronous operations to improve performance and responsiveness. * **Do This:** Mark asynchronous methods with the "async" keyword and use the "await" keyword to asynchronously wait for the completion of tasks. Avoid blocking the thread. Configure "ConfigureAwait(false)" to avoid deadlocks especially when writing reusable libraries. * **Don't Do This:** Use synchronous blocking operations in asynchronous methods, leading to performance bottlenecks. **Why:** Improves the scalability and responsiveness of the system. **Example:** """csharp public class OrderService { private readonly IOrderRepository _orderRepository; public OrderService(IOrderRepository orderRepository) { _orderRepository = orderRepository; } public async Task<Order> GetOrderAsync(Guid orderId) { // Asynchronously retrieve order from the repository return await _orderRepository.GetOrderAsync(orderId).ConfigureAwait(false); } } public interface IOrderRepository { Task<Order> GetOrderAsync(Guid orderId); } public class OrderRepository : IOrderRepository { private readonly OrderContext _context; public OrderRepository(OrderContext context) { _context = context; } public async Task<Order> GetOrderAsync(Guid orderId) { //Asynchronously fetch data from the database return await _context.Orders.FirstOrDefaultAsync(o => o.OrderId == orderId).ConfigureAwait(false); } } """ ### 5.3. Nullable Reference Types **Standard:** Enable and correctly utilize nullable reference types to prevent null reference exceptions and increase code safety. * **Do This**: Enable nullable reference types in your project ("<Nullable>enable</Nullable>" in the ".csproj" file). Annotate reference types with "?" if they can be null. * **Don't Do This**: Ignore nullable warnings. Suppress the warnings without proper null checks. **Why**: Prevents null reference exceptions, improves code clarity, and enhances overall code safety. **Example:** """csharp #nullable enable public class Customer { public string? Name { get; set; } // Name can be null public Address Address { get; set; } // Address cannot be null by default, ensure that address can always be provided during creation } #nullable disable """ ## 6. Common Anti-Patterns * **God Component:** A component with too many responsibilities and dependencies. * **Shared Database:** Components directly accessing the same database tables without clear ownership or synchronization. * **Tight Coupling:** Components depending heavily on each other's internal implementation details. * **Ignoring Domain Language:** Not using the ubiquitous language of the domain in component names and code. * **Premature Optimization:** Optimizing component performance before identifying actual bottlenecks. * **Lack of Testing:** Insufficient unit and integration tests for components. * **Violating Single Responsibility Principle:** Class or module doing more than it should. ## 7. Conclusion Adhering to these component design standards will result in a more maintainable, scalable, and resilient software system. By aligning components with bounded contexts, promoting loose coupling, and utilizing appropriate design patterns, developers can create a system that accurately reflects the business domain and adapts easily to changing requirements. Remember that DDD is an iterative process, and these standards should be refined and adapted as understanding of the domain evolves. Continuous communication with domain experts is essential for ensuring that the software accurately reflects the business needs.
# Core Architecture Standards for Domain Driven Design This document outlines the coding standards for the core architecture of applications built using Domain-Driven Design (DDD) principles. It focuses on establishing a solid foundation for maintainability, scalability, and alignment with business goals. This document reflects current DDD best practices and addresses modern technological approaches. ## 1. Fundamental Architectural Patterns ### 1.1 Layered Architecture **Standard:** Implement a layered architecture to separate concerns and improve maintainability. * **Do This:** Distinguish between the User Interface, Application, Domain, and Infrastructure layers. Ensure each layer only depends on layers directly beneath it. * **Don't Do This:** Create circular dependencies between layers or allow the UI layer to directly access the database. **Why:** Layered architecture promotes separation of concerns, making the application easier to understand, test, and modify. **Code Example (C#):** """csharp // User Interface Layer (Controllers) public class OrderController : ControllerBase { private readonly IOrderService _orderService; public OrderController(IOrderService orderService) { _orderService = orderService; } [HttpPost] public IActionResult CreateOrder(CreateOrderRequest request) { var orderId = _orderService.CreateOrder(request.CustomerId, request.Items); return Ok(orderId); } } // Application Layer (Services) public interface IOrderService { Guid CreateOrder(Guid customerId, List<OrderItemDto> items); } public class OrderService : IOrderService { private readonly IOrderRepository _orderRepository; private readonly ICustomerRepository _customerRepository; public OrderService(IOrderRepository orderRepository, ICustomerRepository customerRepository) { _orderRepository = orderRepository; _customerRepository = customerRepository; } public Guid CreateOrder(Guid customerId, List<OrderItemDto> items) { var customer = _customerRepository.GetById(customerId); if (customer == null) { throw new CustomerNotFoundException(customerId); } var order = Order.Create(customer, items.Select(i => new OrderItem(i.ProductId, i.Quantity)).ToList()); _orderRepository.Add(order); return order.Id; } } // Domain Layer (Entities and Repositories) public class Order { public Guid Id { get; private set; } public Customer Customer { get; private set; } public List<OrderItem> Items { get; private set; } private Order() { } // Required for ORM private Order(Customer customer, List<OrderItem> items) { Id = Guid.NewGuid(); Customer = customer ?? throw new ArgumentNullException(nameof(customer)); Items = items ?? throw new ArgumentNullException(nameof(items)); } public static Order Create(Customer customer, List<OrderItem> items) { return new Order(customer, items); } } public interface IOrderRepository { void Add(Order order); Order? GetById(Guid id); } // Infrastructure Layer (Data Access) public class OrderRepository : IOrderRepository { private readonly AppDbContext _dbContext; public OrderRepository(AppDbContext dbContext) { _dbContext = dbContext; } public void Add(Order order) { _dbContext.Orders.Add(order); _dbContext.SaveChanges(); } public Order? GetById(Guid id) { return _dbContext.Orders.Find(id); } } """ **Anti-Pattern:** The UI layer (e.g., "OrderController") directly interacting with the "AppDbContext" to save orders. ### 1.2 Hexagonal Architecture (Ports and Adapters) **Standard:** Use Hexagonal Architecture to decouple the domain logic from external dependencies such as databases, UI, and messaging systems. * **Do This:** Define ports (interfaces) that represent the interaction points with the domain. Implement adapters that translate between the port interface and the specific technology. * **Don't Do This:** Directly reference concrete infrastructure implementations within the domain. **Why:** Hexagonal architecture makes the application more testable, flexible, and adaptable to changing technologies. It allows you to switch infrastructure concerns without modifying the core domain. **Code Example (Java):** """java // Domain Layer (Core Logic) // Order Management Interface package com.example.domain; public interface OrderManagement { Order createOrder(OrderRequest request); Order getOrder(String orderId); } // Implementation package com.example.domain; import com.example.domain.model.Order; import com.example.domain.model.OrderRequest; import com.example.ports.outbound.OrderPersistence; public class OrderServiceImpl implements OrderManagement { private final OrderPersistence orderPersistence; public OrderServiceImpl(OrderPersistence orderPersistence) { this.orderPersistence = orderPersistence; } @Override public Order createOrder(OrderRequest request) { Order order = new Order(request.getCustomerId(), request.getItems()); orderPersistence.save(order); return order; } @Override public Order getOrder(String orderId) { return orderPersistence.getOrder(orderId); } } // Define Ports (Outbound) package com.example.ports.outbound; import com.example.domain.model.Order; public interface OrderPersistence { Order save(Order order); Order getOrder(String orderId); } // Infrastructure Layer (Adapters) // Adapter for Database package com.example.adapters.outbound; import com.example.domain.model.Order; import com.example.ports.outbound.OrderPersistence; public class OrderDatabaseAdapter implements OrderPersistence { private final OrderRepository orderRepository; public OrderDatabaseAdapter(OrderRepository orderRepository) { this.orderRepository = orderRepository; } @Override public Order save(Order order) { return orderRepository.save(order); } @Override public Order getOrder(String orderId) { return orderRepository.findById(orderId).orElse(null); } } //Spring Data Repository (Example) package com.example.adapters.outbound; import com.example.domain.model.Order; import org.springframework.data.jpa.repository.JpaRepository; public interface OrderRepository extends JpaRepository<Order, String> { } // Configuration package com.example.config; import com.example.adapters.outbound.OrderDatabaseAdapter; import com.example.adapters.outbound.OrderRepository; import com.example.domain.OrderManagement; import com.example.domain.OrderServiceImpl; import com.example.ports.outbound.OrderPersistence; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean public OrderPersistence orderPersistence(OrderRepository orderRepository) { return new OrderDatabaseAdapter(orderRepository); } @Bean public OrderManagement orderManagement(OrderPersistence orderPersistence) { return new OrderServiceImpl(orderPersistence); } } """ **Anti-Pattern:** The "OrderServiceImpl" directly using "JdbcTemplate" to interact with the database. This tightly couples the domain with the database and makes testing difficult. ### 1.3 Clean Architecture **Standard:** Adhere to Clean Architecture principles, ensuring that the domain logic remains independent of frameworks, UI, and external agents. * **Do This:** Structure the application with concentric layers, where the innermost layer represents the domain entities and use cases, and outer layers are responsible for infrastructure concerns. Dependencies should point inwards. * **Don't Do This:** Allow external frameworks or libraries to dictate the structure of the domain logic. **Why:** Clean Architecture maximizes the flexibility, maintainability, and testability of the application by isolating the domain from external forces. **Code Example (TypeScript/Node.js):** """typescript // Domain (Entities and Use Cases) // Entity class Order { constructor(public id: string, public customerId: string, public items: OrderItem[]) {} } class OrderItem { constructor(public productId: string, public quantity: number) {} } // Use Case (Interface) interface CreateOrderUseCase { execute(customerId: string, items: { productId: string; quantity: number }[]): Promise<Order>; } // Use Case (Implementation) class CreateOrder implements CreateOrderUseCase { constructor(private orderRepository: OrderRepository, private customerRepository: CustomerRepository) {} async execute(customerId: string, items: { productId: string; quantity: number }[]): Promise<Order> { const customer = await this.customerRepository.getById(customerId); if (!customer) { throw new Error('Customer not found'); } const order = new Order( uuidv4(), customerId, items.map((item) => new OrderItem(item.productId, item.quantity)) ); await this.orderRepository.save(order); return order; } } // Interface Adapters (Controllers and Gateways) //OrderController import { Request, Response } from 'express'; import { CreateOrderUseCase } from '../../domain/use-cases/create-order'; class OrderController { constructor(private createOrderUseCase: CreateOrderUseCase) {} async createOrder(req: Request, res: Response): Promise<void> { try { const { customerId, items } = req.body; const order = await this.createOrderUseCase.execute(customerId, items); res.status(201).json(order); } catch (error: any) { res.status(400).json({ error: error.message }); } } } // Infrastructure (Repositories) interface OrderRepository { save(order: Order): Promise<void>; getById(id: string): Promise<Order | null>; } // Implementation Example (using MongoDB) class MongoOrderRepository implements OrderRepository { constructor(private db: Db, private collectionName: string = 'orders') {} async save(order: Order): Promise<void> { await this.db.collection(this.collectionName).insertOne(order); } async getById(id: string): Promise<Order | null> { const result = await this.db.collection(this.collectionName).findOne({ id: id }); return result as Order | null; } } """ **Anti-Pattern:** Import Express.js-specific objects (like "Request", "Response") directly into the use case layer. ## 2. Project Structure and Organization ### 2.1 Package/Namespace Organization **Standard:** Organize the project into packages/namespaces that reflect the layered architecture and the domain's bounded contexts. * **Do This:** Create separate packages/namespaces for each layer (e.g., "Application", "Domain", "Infrastructure"). Within the domain layer, further organize based on bounded contexts (e.g., "Sales", "Inventory", "Shipping"). * **Don't Do This:** Put all classes in a single package or namespace, or mix classes from different layers in the same package. **Why:** Clear package organization improves code discoverability, reduces naming conflicts, and reinforces the architectural boundaries. **Example (Maven/Java):** """ src/main/java/ com/example/ application/ OrderService.java ... domain/ model/ Order.java OrderItem.java ... service/ OrderManagement.java ... repository/ OrderRepository.java //Interface definition only ... infrastructure/ persistence/ JpaOrderRepository.java //Implementation using JPA ... interfaces/ rest/ OrderController.java ... """ **Anti-Pattern:** Mixing domain entities ("Order.java") directly with persistence implementations ("JpaOrderRepository.java") in the same package. ### 2.2 Module Boundaries **Standard:** Enforce module boundaries to encapsulate implementation details and control dependencies between different parts of the application. * **Do This:** Use language-specific features like Java modules (Jigsaw) or C# internal access modifiers to restrict access to internal classes and methods. * **Don't Do This:** Expose internal implementation details through public APIs. **Why:** Module boundaries promote encapsulation, reduce coupling, and allow for independent evolution of different parts of the application. **Example (.NET/C#):** """csharp // Domain assembly namespace MyCompany.MyApplication.Domain { public class Order // Public, part of the public API { internal OrderValidationService Validator {get; set;} // Internal, only visible within the Domain assembly } internal class OrderValidationService { // Implementation details for order validation } } // Application assembly (references Domain assembly) namespace MyCompany.MyApplication.Application { public class OrderService { public void CreateOrder(Order order) { // Can access public Order class // Cannot directly access internal OrderValidationService } } } """ **Anti-Pattern:** Making the "OrderValidationService" public, thus exposing internal domain logic to the application layer, breaking encapsulation. ### 2.3 Anti-Corruption Layer **Standard:** When integrating with external systems, use an Anti-Corruption Layer (ACL) to prevent external data models or APIs from polluting the domain model. * **Do This:** Create a translation layer that converts data from the external system into the domain model, and vice versa. * **Don't Do This:** Directly use external data models or APIs within the domain logic. **Why:** An ACL protects the domain from changes in external systems, ensuring the domain remains stable and focused on the business logic. **Code Example (Python):** """python # External System Model class ExternalCustomer: def __init__(self, ext_id, full_name, address_line_1): self.ext_id = ext_id self.full_name = full_name self.address_line_1 = address_line_1 # Domain Model class Customer: def __init__(self, customer_id, name, address): self.customer_id = customer_id self.name = name self.address = address class Address: def __init__(self, street): self.street = street # Anti-Corruption Layer class CustomerTranslator: @staticmethod def to_domain(external_customer: ExternalCustomer) -> Customer: customer_id = external_customer.ext_id #Remap external ID name = external_customer.full_name address = Address(external_customer.address_line_1) return Customer(customer_id, name, address) # Usage external_customer_data = ExternalCustomer("EXT123", "John Doe", "123 Main St") customer = CustomerTranslator.to_domain(external_customer_data) print(customer.name) # John Doe """ **Anti-Pattern:** Directly using the "ExternalCustomer" object within the domain logic, tightly coupling the domain to the external system. The use of static methods can also be debated here; using a "CustomerTranslator" object injected as a dependency might be better depending on complexity. ## 3. Modern Approaches and Patterns ### 3.1 Microservices Architecture (Context Boundaries) **Standard:** When using a microservices architecture, align service boundaries with the bounded contexts identified during domain analysis. * **Do This:** Design each microservice to be responsible for a single bounded context. Ensure each service has its own database and domain model. * **Don't Do This:** Create large, monolithic services that span multiple bounded contexts. **Why:** Microservices aligned with bounded contexts promote autonomy, independent deployment, and scalability. ### 3.2 Event-Driven Architecture (Domain Events) **Standard:** Leverage domain events to decouple services and facilitate communication within and between bounded contexts. * **Do This:** Raise domain events when significant state changes occur within the domain. Use a message broker (e.g., RabbitMQ, Kafka) to distribute events asynchronously to interested subscribers. * **Don't Do This:** Tightly couple services by directly invoking methods on other services. **Why:** Event-driven architecture improves scalability, resilience, and flexibility by decoupling services and enabling asynchronous communication. **Code Example (Python with Celery/RabbitMQ):** """python # Domain Event class OrderCreatedEvent: def __init__(self, order_id, customer_id): self.order_id = order_id self.customer_id = customer_id # Application Layer (Raise Event) from celery import Celery celery_app = Celery('tasks', broker='pyamqp://guest@localhost//') def create_order(customer_id, items): order = Order(customer_id, items) # ... persist order ... event = OrderCreatedEvent(order.id, customer_id) publish_order_created_event.delay(event.__dict__) # serialize the event as a dictionary return order @celery_app.task def publish_order_created_event(event_data): # Simulate publishing to a message queue print(f"Published OrderCreatedEvent: {event_data}") # In a real implementation, you'd use a library to publish to RabbitMQ # e.g., pika, kombu """ **Anti-Pattern:** Directly calling a method on the "ShippingService" from the "OrderService" to initiate shipment after order creation. ### 3.3 CQRS (Command Query Responsibility Segregation) **Standard:** Implement CQRS to separate read and write operations, allowing for optimized data models and scaling strategies for each. * **Do This:** Create separate models and data stores for commands (write operations) and queries (read operations). Use DTOs to transfer data between layers. * **Don't Do This:** Use the same domain model for both reads and writes, leading to performance bottlenecks and complexity. **Why:** CQRS allows for independent scaling and optimization of read and write operations. **Code Example (Go):** """go // Command Model package command import "fmt" type CreateOrderCommand struct { CustomerID string Items []OrderItem } type OrderItem struct { ProductID string Quantity int } type OrderCommandHandler struct { OrderRepository OrderCommandRepository } func (h *OrderCommandHandler) Handle(cmd CreateOrderCommand) error { fmt.Printf("Handling create order command: %v\n", cmd) err := h.OrderRepository.Save(cmd) if err != nil { return fmt.Errorf("failed to save order: %w", err) } return nil } type OrderCommandRepository interface { Save(cmd CreateOrderCommand) error } // Query Model package query type Order struct { OrderID string CustomerID string Items []OrderItem } type OrderItem struct { ProductID string Quantity int } type OrderQuery struct { OrderID string } type OrderQueryHandler struct { OrderRepository OrderQueryRepository } func (h *OrderQueryHandler) Handle(query OrderQuery) (Order, error) { fmt.Printf("Handling order query: %v\n", query) order, err := h.OrderRepository.Get(query.OrderID) if err != nil { return Order{}, fmt.Errorf("failed to retrieve order: %w", err) } return order, nil } type OrderQueryRepository interface { Get(orderID string) (Order, error) } """ **Anti-Pattern:** Using the same "Order" data model for both writing (creating/updating) and reading order data. ### 3.4 Bounded Contexts **Standard**: Explicitly define bounded contexts to manage complexity by creating explicit semantic boundaries within the domain. * **Do This**: Identify ubiquitous language within each context, and model elements accordingly. Ensure each context has its own, isolated data. * **Don't Do This**: Allow models to bleed across context boundaries, leading to ambiguity and tight coupling. **Why**: Bounded contexts mitigate complexity, improve team autonomy, and encourage better alignment with business needs. ## 4. Technology-Specific Details ### Example: Spring Boot (Java) * Use Spring Data JPA for repository implementation with proper use of interfaces. * Apply Spring's "@Transactional" annotation to methods in the Application Layer to manage transactions. * Ensure proper exception handling using "@ControllerAdvice" for centralized error handling and conversion to appropriate HTTP responses. ### Example: .NET (C#) * Utilize Entity Framework Core for database interactions, ensuring proper configuration of DbContext and migrations. * Implement MediatR for handling commands and queries, providing a clean separation of concerns. * Use FluentValidation for data validation to ensure data integrity. ## 5. Conclusion These coding standards for Core Architecture in Domain-Driven Design provide a solid foundation for building maintainable, scalable, and business-aligned applications. By adhering to these guidelines, development teams can ensure a consistent and high-quality codebase that supports the long-term evolution of the system. Remember that the key is to understand *why* these standards exist and apply them thoughtfully in context.
# State Management Standards for Domain Driven Design This document outlines coding standards for state management within Domain-Driven Design (DDD) applications. It aims to provide clear guidance on how to manage application state, data flow, and reactivity while adhering to DDD principles. This ensures maintainability, performance, and security. ## 1. Introduction to State Management in DDD State management in DDD contexts refers to how the application persists, retrieves, and modifies the state of domain entities and aggregates over time. Effective state management is critical for ensuring data consistency, supporting complex business logic, and enabling scalability and resilience. It's not just about CRUD operations; it's about reflecting the evolving state of the domain accurately. ## 2. Core Principles * **Ubiquitous Language:** State representations should align with the Ubiquitous Language defined by the domain experts. * **Data Integrity:** Ensure that the state of aggregates and entities remains consistent and valid according to domain rules. * **Persistence Ignorance:** Domain models shouldn't be tightly coupled to persistence mechanisms. Repositories handle persistence concerns. * **Eventual Consistency:** In distributed systems, embrace eventual consistency, using Domain Events to propagate state changes. * **Immutability:** Favor immutable data structures where appropriate to simplify reasoning about state changes. * **Reactive Principles:** Use reactive approaches where real-time updates and responsiveness are required. ## 3. Repository Pattern The Repository pattern mediates between the domain and data mapping layers, providing a collection-like interface for accessing domain objects. ### 3.1. Standard: Repository Interface Definition * **Do This:** Define repository interfaces within the domain layer. * **Don't Do This:** Expose persistence-specific details (e.g., database queries) in the domain layer. **Why:** Keeps the domain model independent of any specific persistence technology and promotes testability. **Code Example (C#):** """csharp // Domain Layer public interface IOrderRepository { Order GetById(Guid id); IEnumerable<Order> GetAll(); void Add(Order order); void Update(Order order); void Delete(Order order); } // Infrastructure Layer (Implementation) public class OrderRepository : IOrderRepository { private readonly AppDbContext _dbContext; public OrderRepository(AppDbContext dbContext) { _dbContext = dbContext; } public Order GetById(Guid id) { return _dbContext.Orders.Find(id); } public IEnumerable<Order> GetAll() { return _dbContext.Orders.ToList(); } public void Add(Order order) { _dbContext.Orders.Add(order); _dbContext.SaveChanges(); } public void Update(Order order) { _dbContext.Orders.Update(order); _dbContext.SaveChanges(); } public void Delete(Order order) { _dbContext.Orders.Remove(order); _dbContext.SaveChanges(); } } """ ### 3.2. Standard: Repository Methods * **Do This:** Expose methods that align with domain operations (e.g., "GetOpenOrders", "FindOrdersByCustomer"). * **Don't Do This:** Create generic CRUD repositories. Specialized repositories are preferred. **Why:** Generic CRUD repositories often leak domain knowledge into the infrastructure layer. **Code Example (Java):** """java // Domain Layer public interface OrderRepository { Order findById(UUID id); List<Order> findByCustomerId(UUID customerId); List<Order> findOpenOrders(); void save(Order order); void delete(Order order); } // Infrastructure Layer (Implementation with Spring Data JPA) @Repository public interface JpaOrderRepository extends OrderRepository, JpaRepository<Order, UUID> { List<Order> findByCustomerId(UUID customerId); @Query("SELECT o FROM Order o WHERE o.status = 'OPEN'") List<Order> findOpenOrders(); } """ ### 3.3. Anti-Pattern: Active Record in Domain Entities * **Avoid:** Directly embedding persistence logic within domain entities (Active Record pattern). * **Why:** Violates the principle of persistence ignorance and makes testing difficult. Domain entities become tightly coupled to the database. ### 3.4 Technology-Specific Details (Entity Framework Core): * Use "AsNoTracking()" when querying for read-only operations to improve performance. * Configure relationships using Fluent API instead of data annotations within domain entities. Keep entities clean. """csharp //Example using AsNoTracking() public Order GetReadOnlyOrder(Guid id) { return _dbContext.Orders.AsNoTracking().FirstOrDefault(o => o.Id == id); } """ ## 4. Domain Events Domain Events represent significant occurrences within the domain. They facilitate communication between aggregates and allow for decoupling of business logic. ### 4.1. Standard: Event Definition * **Do This:** Define domain events as immutable classes representing a past event. * **Don't Do This:** Include mutable state within domain events. **Why:** Immutability ensures that events accurately reflect the state at the time of the event and prevents unintended side effects. Events represent *what* happened, not *what is happening now*. **Code Example (C#):** """csharp // Domain Layer public class OrderCreatedEvent : INotification { public Guid OrderId { get; } public Guid CustomerId { get; } public DateTime CreatedDate { get; } public OrderCreatedEvent(Guid orderId, Guid customerId, DateTime createdDate) { OrderId = orderId; CustomerId = customerId; CreatedDate = createdDate; } } """ ### 4.2. Standard: Publishing Events * **Do This:** Publish domain events after a state change occurs within an aggregate. * **Don't Do This:** Directly call event handlers within the aggregate. Use a mediator pattern or similar mechanism for decoupling. **Why:** Decoupling allows event handlers to be added or removed without modifying the core domain logic. **Code Example (C# with MediatR):** """csharp // Domain Layer (Aggregate Root) public class Order { public Guid Id { get; private set; } public Guid CustomerId { get; private set; } public OrderStatus Status { get; private set; } private readonly List<INotification> _domainEvents = new List<INotification>(); public IReadOnlyCollection<INotification> DomainEvents => _domainEvents.AsReadOnly(); public Order(Guid customerId) { Id = Guid.NewGuid(); CustomerId = customerId; Status = OrderStatus.Created; _domainEvents.Add(new OrderCreatedEvent(Id, CustomerId, DateTime.UtcNow)); } public void MarkAsShipped() { if (Status != OrderStatus.Created) { throw new InvalidOperationException("Order must be in Created status."); } Status = OrderStatus.Shipped; _domainEvents.Add(new OrderShippedEvent(Id)); } public void ClearDomainEvents() { _domainEvents.Clear(); } } //Application Layer (Using MediatR to publish events) public class OrderService { private readonly IOrderRepository _orderRepository; private readonly IMediator _mediator; public OrderService(IOrderRepository orderRepository, IMediator mediator) { _orderRepository = orderRepository; _mediator = mediator; } public async Task CreateOrder(Guid customerId) { var order = new Order(customerId); _orderRepository.Add(order); await _mediator.Publish(new OrderCreatedEvent(order.Id, order.CustomerId, DateTime.UtcNow)); //Publish the Domain Event _orderRepository.Update(order); // Save aggregate state *after* publishing events if using outbox } } // Event Handler Example public class OrderCreatedEventHandler : INotificationHandler<OrderCreatedEvent> { private readonly ILogger<OrderCreatedEventHandler> _logger; public OrderCreatedEventHandler(ILogger<OrderCreatedEventHandler> logger) { _logger = logger; } public async Task Handle(OrderCreatedEvent notification, CancellationToken cancellationToken) { // Log the order creation _logger.LogInformation($"Order {notification.OrderId} created for customer {notification.CustomerId}"); await Task.CompletedTask; //Potentially perform integrations, notifications etc. } } """ ### 4.3. Standard: Outbox Pattern * **Do This:** Use the Outbox Pattern for reliable event delivery, especially when dealing with distributed transactions. * **Explanation:** The Outbox pattern stores domain events in the same transaction as the aggregate state changes. A separate process then reliably publishes these events to a message broker. **Code Example (Conceptual C#):** """csharp // 1. Add Domain Event to Outbox Table within the same transaction as Order creation using (var transaction = _dbContext.Database.BeginTransaction()) { try { var order = new Order(customerId); _orderRepository.Add(order); // Save Domain Event to Outbox var outboxMessage = new OutboxMessage { Id = Guid.NewGuid(), OccurredOn = DateTime.UtcNow, Type = typeof(OrderCreatedEvent).FullName, Data = JsonConvert.SerializeObject(new OrderCreatedEvent(order.Id, order.CustomerId, DateTime.UtcNow)), ProcessedDate = null //Not processed yet }; _dbContext.OutboxMessages.Add(outboxMessage); _dbContext.SaveChanges(); transaction.Commit(); } catch (Exception ex) { transaction.Rollback(); //Handle Exception } } // 2. Background Worker processes outbox messages public class OutboxProcessor : BackgroundService { private readonly IServiceProvider _serviceProvider; public OutboxProcessor(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { using (var scope = _serviceProvider.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); var mediator = scope.ServiceProvider.GetRequiredService<IMediator>(); var messages = dbContext.OutboxMessages .Where(m => m.ProcessedDate == null) .OrderBy(m => m.OccurredOn) .Take(20) //Batch Size .ToList(); foreach (var message in messages) { //Publish Domain Event var eventType = Type.GetType(message.Type); var eventData = JsonConvert.DeserializeObject(message.Data, eventType) as INotification; await mediator.Publish(eventData, stoppingToken); //Mark Message as Processed message.ProcessedDate = DateTime.UtcNow; } await dbContext.SaveChangesAsync(stoppingToken); } await Task.Delay(5000, stoppingToken); //Polling Interval } } } //3. OutboxMessage entity public class OutboxMessage { public Guid Id { get; set; } public string Type { get; set; } public string Data { get; set; } public DateTime OccurredOn { get; set; } public DateTime? ProcessedDate { get; set; } } """ ### 4.4. Anti-Pattern: Ignoring Domain Events * **Avoid:** Performing side effects directly within aggregates without raising domain events. This creates tight coupling. ## 5. Snapshots Snapshots capture the state of an aggregate at a specific point in time. They can optimize performance by reducing the need to replay all events since the aggregate's creation when retrieving it. ### 5.1. Standard: Snapshot Frequency * **Consider:** Implementing snapshots for aggregates with a large number of events, taking them periodically (e.g., after every 100th event). * **Rationale:** Significantly speeds up aggregate reconstruction, especially when dealing with a long event history. ### 5.2. Standard: Snapshot Storage * **Store:** Snapshots in a separate store from the event stream, optimized for quick retrieval of the latest snapshot. * **Explanation:** A dedicated snapshot store allows the event store to focus on event append operations, while the snapshot store provides fast read access. """csharp //Example Snapshot Entity public class OrderSnapshot { public Guid AggregateId { get; set; } public int Version { get; set; } public string Data { get; set; } //Serialized State public DateTime CreatedAt {get; set;} } """ ## 6. CQRS (Command Query Responsibility Segregation) CQRS separates read and write operations for improved performance and scalability. ### 6.1. Standard: Separate Models * **Do This:** Maintain separate read (query) models that are optimized for specific UI or reporting requirements. * **Don't Do This:** Directly expose the domain model for querying. **Why:** Read models can be denormalized and optimized for specific queries, avoiding complex joins and improving performance. Domain models are designed for behavior and consistency; query models are designed for retrieval. ### 6.2. Standard: Materialized Views * **Use:** Materialized views to pre-compute query results and store them for fast retrieval. * **Update:** Update materialized views asynchronously based on domain events. * **Example:** Using a message queue like RabbitMQ to update read models ensures eventual consistency. ### 6.3. Standard: Asynchronous Updates * **Update:** Read models asynchronously in response to domain events to ensure that the write side is not impacted by the update process of read models. **Code example (Conceptual):** """csharp //OrderCreatedEventHandler (from Domain Event section) public class OrderCreatedEventHandler : INotificationHandler<OrderCreatedEvent> { private readonly IReadOrderRepository _readOrderRepository; //Repository for the READ model public OrderCreatedEventHandler(IReadOrderRepository readOrderRepository) { _readOrderRepository = readOrderRepository; } public async Task Handle(OrderCreatedEvent notification, CancellationToken cancellationToken) { //Update the Read Model asynchronously var readOrder = new ReadOrder { OrderId = notification.OrderId, CustomerId = notification.CustomerId, CreatedDate = notification.CreatedDate }; await _readOrderRepository.Add(readOrder); } } //Example Read Model public class ReadOrder { public Guid OrderId { get; set; } public Guid CustomerId { get; set; } public DateTime CreatedDate { get; set; } //Potentially other data denormalized for read purposes } """ ### 6.4. Anti-Pattern: Synchronous Read Model Updates * **Avoid:** Updating read models synchronously within command handlers. This can negatively impact the responsiveness of the application. ## 7. Immutability ### 7.1. Standard: Immutable Value Objects * **Do This:** Define value objects as immutable classes or records. * **Benefits:** Immutability simplifies reasoning, prevents unintended side effects, and improves thread safety. **Code Example (C# Record):** """csharp // Domain Layer public record Address(string Street, string City, string State, string ZipCode); """ ### 7.2. Standard: Immutable Event Payloads * **Do This:** Ensure that domain events carry immutable payloads. ## 8. Concurrency and Consistency ### 8.1. Optimistic Concurrency * **Use:** Optimistic concurrency control to handle concurrent updates to aggregates. * **Implementation:** Include a version property in the aggregate and check for changes during updates. **Code Example (C#):** """csharp // Aggregate Root public class Order { public Guid Id { get; private set; } public int Version { get; private set; } // Optimistic Concurrency public void UpdateShippingAddress(string newAddress) { //Business Logic Version++; } } //Repository public void Update(Order order) { _dbContext.Entry(order).Property("Version").OriginalValue = order.Version - 1; // Set original value for concurrency check try { _dbContext.Orders.Update(order); _dbContext.SaveChanges(); } catch (DbUpdateConcurrencyException ex) { throw new ConcurrencyException("The order has been modified by another user."); } } """ ### 8.2. Standard: Eventual Consistency Strategies * For distributed systems that span multiple bounded contexts, embrace eventual consistency, using Domain Events to propagate state changes. ## 9. Reactivity ### 9.1. Standard: Reactive Streams * **Use:** Reactive Streams (e.g., RxJava, Reactor) for handling real-time data updates and asynchronous event processing. * **Benefits:** Improve responsiveness and scalability by processing events non-blocking. ### 9.2. Standard: Backpressure * **Implement:** Backpressure mechanisms to prevent overwhelming consumers of reactive streams. * RxJava and Reactor frameworks natively support backpressure strategies like buffering, dropping, or erroring. * **Explanation:** Ensures that consumers can handle the incoming event rate, preventing performance degradation or failure. ## 10. Testing ### 10.1. Standard: Unit Tests * **Test:** Domain entities and value objects thoroughly with unit tests to ensure they behave as expected and maintain state correctly. * **Focus:** Boundary conditions and state transitions. ### 10.2. Standard: Integration Tests * **Test:** Repositories with integration tests to verify that they correctly persist and retrieve domain objects. * **Use:** In-memory databases or test containers for isolated testing. ### 10.3. Standard: Eventual Consistency Tests * **Test:** Eventual consistency scenarios with integration tests that simulate asynchronous event processing. May require polling or retry mechanisms to confirm state updates. ## 11. Security Implications ### 11.1. Standard: Secure State Transitions * **Ensure:** That all state transitions within aggregates are protected by appropriate authorization checks. * **Prevent:** Unauthorized modifications of the state. ### 11.2. Standard: Data Encryption * **Encrypt:** Sensitive data at rest in the database and during transmission over the network. ## 12. Performance Considerations * Use lazy loading strategically in Entity Framework Core or other ORMs, particularly on aggregate roots, if their child entities are not always required. Overuse can lead to N+1 query problems. * Implement caching strategies at different levels (e.g., in-memory, distributed cache) for frequently accessed read models. * Optimize database indexes for commonly used query patterns in repositories and read models. ## 13. Conclusion Adhering to these state management standards is critical for building robust, maintainable, and scalable DDD applications. By carefully considering data consistency, decoupling the domain from persistence concerns, and leveraging patterns like Domain Events and CQRS, developers can create software that accurately reflects the complexities of the business domain and adapts effectively to change. Remember to tailor these guidelines to the specific needs of your project and domain, and continuously refine them as your understanding evolves.
# Performance Optimization Standards for Domain Driven Design This document establishes coding standards for performance optimization within a Domain-Driven Design (DDD) context. These standards aim to improve application speed, responsiveness, and resource usage, leveraging modern approaches and the latest DDD principles. This document is intended to guide developers and inform AI coding assistants. ## 1. Architectural Considerations for Performance ### 1.1. Bounded Context Decomposition **Standard:** Decompose the system into loosely coupled Bounded Contexts with well-defined interfaces. **Do This:** Identify core business capabilities and represent them as independent Bounded Contexts. **Don't Do This:** Create monolithic applications with a single, large domain model. **Why:** This reduces the scope of data access and computation within each context, improving performance and scalability. Each bounded context can be independently optimized without affecting others. **Code Example (Conceptual):** """ // Monolithic Application (Anti-pattern) class OrderService { // Handles order processing, customer management, and inventory updates } // DDD with Bounded Contexts namespace Ordering { class OrderService { // Deals solely with order-related logic } } namespace Inventory { class InventoryService { // Deals solely with inventory-related logic } } namespace CustomerManagement { class CustomerService { // Deals solely with customer-related logic } } """ ### 1.2. Strategic Event Handling **Standard:** Utilize Domain Events strategically for asynchronous processing and decoupling. **Do This:** Publish Domain Events for non-critical side effects (e.g., sending notifications, updating reports). **Don't Do This:** Perform all operations synchronously within a single transaction or service call. **Why:** Asynchronous event handling moves complex operations out of the main request thread, improving responsiveness. **Code Example (C#):** """csharp // Synchronous (Anti-pattern) public class OrderService { public void PlaceOrder(Order order) { // Process order // ... // Send email confirmation (slow and blocking) SendConfirmationEmail(order.CustomerEmail); } private void SendConfirmationEmail(string email) { /* Implementation */ } } // Asynchronous with Domain Events public class OrderPlacedEvent : INotification { public Order Order { get; set; } } public class OrderService { private readonly IPublisher _publisher; public OrderService(IPublisher publisher) { _publisher = publisher; } public async Task PlaceOrder(Order order) { // Process order // ... // Publish event for asynchronous handling await _publisher.Publish(new OrderPlacedEvent { Order = order }); } } public class OrderPlacedEventHandler : INotificationHandler<OrderPlacedEvent> { public async Task Handle(OrderPlacedEvent notification, CancellationToken cancellationToken) { // Asynchronously send email confirmation await SendConfirmationEmail(notification.Order.CustomerEmail); } private async Task SendConfirmationEmail(string email) { /* await Implementation */ } } """ ### 1.3. CQRS (Command Query Responsibility Segregation) **Standard:** Employ CQRS to separate read and write operations for high-performance querying. **Do This:** Create separate data models for read and write operations. Optimize read models for specific query needs. **Don't Do This:** Use the same domain model for both reads and writes, especially for complex read scenarios. **Why:** Tailored read models significantly improve query performance. Write models can focus on consistency and transaction management. **Code Example (Conceptual - C#):** """csharp // Simplified Example // Command (Write) Model public class Order { public Guid Id { get; set; } public List<OrderItem> Items { get; set; } // Aggregate root relationships // ... Domain Logic } //Query (Read) Model public class OrderSummary { public Guid Id { get; set; } public string CustomerName { get; set; } public decimal TotalAmount { get; set; } //Optimized for listing orders } """ ## 2. Domain Model Optimization ### 2.1. Lazy Loading & Eager Loading **Standard:** Use lazy loading for related entities, and eager loading for entities frequently accessed together. **Do This:** Configure lazy loading for optional relationships and eager loading in specific queries where related data is needed. **Don't Do This:** Eager load all relationships, as it can lead to significant performance degradation ("over-fetching"). Don't rely solely on lazy loading as the database "chatters". **Why:** Balances data retrieval needs and avoids unnecessary database round trips. **Code Example (Entity Framework Core - C#):** """csharp // Lazy Loading (Requires proxy generation) public class Customer { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<Order> Orders { get; set; } // Lazy-loaded collection } // Eager Loading using (var context = new ApplicationDbContext()) { var customer = context.Customers .Include(c => c.Orders) // Eagerly load Orders .FirstOrDefault(c => c.Id == customerId); // Accessing customer.Orders will not trigger a new database query } // Projection to DTO for optimized reading using (var context = new ApplicationDbContext()) { var customerDto = context.Customers .Where(c => c.Id == customerId) .Select(c => new CustomerDto { Id = c.Id, Name = c.Name, OrderCount = c.Orders.Count() }) .FirstOrDefault(); } """ ### 2.2. Value Objects & Immutability **Standard:** Utilize Value Objects extensively for representing domain concepts and enforce immutability. **Do This:** Create Value Objects for address, monetary amount, and other descriptive attributes. Immutability: Ensure that value objects cannot be changed after creation. **Don't Do This:** Use primitive types directly or create mutable domain objects, leading to shared state and potential performance issues with concurrency. **Why:** Immutability simplifies reasoning about state, eliminates the need for defensive copying, and improves concurrency. **Code Example (C#):** """csharp public class Address { public string Street { get; } public string City { get; } public string PostalCode { get; } public Address(string street, string city, string postalCode) { Street = street; City = city; PostalCode = postalCode; } } """ ### 2.3. Aggregate Boundaries **Standard:** Design aggregates around transactional consistency, but keep them as small as possible. **Do This:** Ensure that operations within an aggregate maintain consistency. Avoid creating large aggregates that encompass unrelated entities. **Don't Do This:** Create bloated aggregates, leading to excessive locking and performance bottlenecks. **Why:** Properly scoped aggregates reduce contention and improve concurrency. Limit the scope of operations that require a transaction. ## 3. Data Access Optimization ### 3.1. Compiled Queries **Standard:** Use compiled queries (e.g., with Entity Framework Core) for frequently executed queries. **Do This:** Compile queries that are executed repeatedly to reduce parsing and compilation overhead. **Don't Do This:** Use dynamic queries where they are unnecessary or compile every query - only compile those run very often. **Why:** Compiled queries cache the query execution plan, improving performance for subsequent executions. **Code Example (Entity Framework Core - C#):** """csharp private static readonly Func<ApplicationDbContext, int, Customer> _getCustomerById = EF.CompileQuery((ApplicationDbContext context, int customerId) => context.Customers.FirstOrDefault(c => c.Id == customerId)); using (var context = new ApplicationDbContext()) { var customer = _getCustomerById(context, customerId); //Fast execution } """ ### 3.2. Connection Pooling **Standard:** Leverage connection pooling provided by the database provider. **Do This:** Ensure that connection strings are configured to enable connection pooling. Use dependency injection to share context instances rather than creating new expensive ones. **Don't Do This:** Open and close database connections frequently; let the connection pool manage connections. **Why:** Connection pooling reduces the overhead of establishing and closing database connections. ### 3.3. Appropriate Indexing **Standard:** Create appropriate indexes on database columns used in common queries. **Do This:** Analyze query execution plans to identify missing indexes and create them accordingly. Consider composite indexes for multi-column queries. **Don't Do This:** Add too many indexes, as they can slow down write operations. **Why:** Indexes improve query performance by allowing the database to quickly locate relevant data. ### 3.4. Asynchronous Data Access **Standard:** Use asynchronous methods for data access operations where possible. **Do This:** Use "async" and "await" keywords for database operations to avoid blocking threads. **Don't Do This:** Perform synchronous data access in request threads, leading to thread starvation. **Code Example (Entity Framework Core - C#):** """csharp public async Task<Customer> GetCustomerAsync(int customerId) { using (var context = new ApplicationDbContext()) { return await context.Customers.FindAsync(customerId); // Asynchronous operation } } """ ### 3.5 Data Transfer Objects (DTOs) **Standard:** Returning DTOs instead of domain entities. **Do This:** Project the domain entities to flat DTOs before returning data to client or other bounded contexts. **Don't Do This:** Serializing the entire graph of domain entities, exposing internal details or unnecessarily large payloads. **Why:** DTOs allow shaping the data for a specific purpose, decreasing the payload and potential for security issues in exposing domain internals. **Code Example (C#):** """csharp public class CustomerDto { public Guid Id {get; set;} public string Name { get; set; } public string Email { get; set; } } // Service to provide customer list as DTOs public async Task<IEnumerable<CustomerDto>> GetCustomersAsync() { using(var context = new ApplicationDbContext()) { return await context.Customers.Select(c => new CustomerDto{ Id = c.Id, Name = c.Name, Email = c.Email }).ToListAsync(); } } """ ## 4. Caching Strategies ### 4.1. Layered Caching **Standard:** Implement caching at different layers of the application architecture. **Do This:** Use in-memory caching (e.g., "MemoryCache" in .NET) for frequently accessed data. Implement distributed caching (e.g., Redis, Memcached) for shared data across multiple instances. Consider caching at the data access layer. **Don't Do This:** Cache sensitive information without proper security measures. Over-cache, leading to stale data. **Why:** Reduces database load and improves response times. **Code Example (In-Memory Caching - C#):** """csharp private readonly IMemoryCache _cache; public CustomerService(IMemoryCache cache) { _cache = cache; } public async Task<Customer> GetCustomerAsync(int customerId) { string cacheKey = $"customer-{customerId}"; if (!_cache.TryGetValue(cacheKey, out Customer customer)) { using (var context = new ApplicationDbContext()) { customer = await context.Customers.FindAsync(customerId); var cacheEntryOptions = new MemoryCacheEntryOptions() .SetSlidingExpiration(TimeSpan.FromSeconds(60)); _cache.Set(cacheKey, customer, cacheEntryOptions); } } return customer; } """ ### 4.2. Cache Invalidation **Standard:** Implement effective cache invalidation strategies. **Do This:** Use time-based expiration, event-based invalidation, or a combination of both. Be aware of cache stampedes. **Don't Do This:** Rely solely on time-based expiration, as it can lead to stale data. Neglect cache invalidation, leading to inconsistencies. **Why:** Ensures that cached data remains consistent with the underlying data source. ## 5. Code Level Optimizations ### 5.1 Avoid Boxing/Unboxing **Standard:** Reduce Boxing/Unboxing operations. **Do This:** Use Generics instead of "object". Consider "struct" for small value types where value semantics are required. **Don't Do This:** Use non-generic collections such as ArrayList causing boxing and unboxing of value types. **Why:** Boxing and unboxing introduce performance overhead. ### 5.2 String Concatenation: StringBuilder **Standard:** Use "StringBuilder" for string manipulation operations. **Do This:** Create string using "StringBuilder" when concatenating strings in loops or repeatedly modifying the same string, avoid creating temporary string instances. **Don't Do This:** Repeatedly concatenating strings with the "+" operator can lead to performance issues, especially within loops. **Why:** "String" class in .NET is immutable. Using "+" creates copies on each operation. ### 5.3 Use "HashSet" for lookups **Standard:** Use "HashSet<T>" when verifying the presence of items in a set. **Do This:** Store lookup tables in a "HashSet<T>". "HashSet" implements hash-based lookup which provides a faster "O(1)" lookup time (average case) compared to "List.Contains(item)" that takes "O(n)". **Don't Do This:** Use ".Contains()" method on a list, particularly in scenarios where performance is critical. **Why:** Using "HashSet<T>" improves performance significantly. ## 6. Monitoring and Profiling ### 6.1. Performance Monitoring **Standard:** Implement performance monitoring to track key metrics. **Do This:** Use tools like Application Insights or Prometheus to monitor response times, database query times, and resource utilization. **Don't Do This:** Operate in the dark without monitoring your application's performance. **Why:** Allows you to identify performance bottlenecks and proactively address them. ### 6.2. Profiling **Standard:** Use profiling tools to identify performance bottlenecks in the code. **Do This:** Use profilers like JetBrains dotTrace or Visual Studio Profiler to analyze code execution and identify slow methods or memory leaks. **Don't Do This:** Guess where the performance bottlenecks are; use profiling data to guide your optimizations. **Why:** Provides detailed insights into code execution and allows you to target optimizations effectively. ## 7. Technology-Specific Considerations ### 7.1. .NET Performance * **Standard:** Utilize ValueTask for async methods that might complete synchronously. * **Standard:** Use "Span<T>" / "Memory<T>" for high-performance data slicing. * **Standard:** Use System.Text.Json for serialization, providing better performance. ### 7.2. Database Performance * **Standard:** Optimize database queries using execution plans. * **Standard:** Utilize database-specific features such as stored procedures for complex operations. ## 8. Continuous Improvement **Standard:** Regularly review and refine performance optimization strategies. **Do This:** Conduct performance audits, analyze monitoring data, and update coding standards based on new findings. **Don't Do This:** Treat performance optimization as a one-time task; continuous improvement is key. **Why:** Ensures that the application remains performant as it evolves.
# Testing Methodologies Standards for Domain Driven Design This document outlines the coding standards for testing methodologies in Domain Driven Design (DDD). It provides guidelines for unit, integration, and end-to-end testing within a DDD context, emphasizing maintainability, performance, and security. These standards are designed to ensure that tests are effective, reliable, and aligned with the principles of DDD. By adhering to these practices, development teams can create robust and well-tested domain models. ## 1. General Testing Principles in DDD ### 1.1. Standard: Focus on Domain Logic **Do This:** Prioritize testing domain logic encapsulated in entities, value objects, domain services, and aggregates. **Don't Do This:** Neglect testing complex interactions and business rules within the domain layer in favor of simple CRUD operations. **Why:** Domain logic is the heart of the application in DDD. Comprehensive testing ensures that business rules are correctly implemented and that the domain model behaves as expected. This focus leads to more reliable and maintainable software. **Example:** """csharp // Correct: Testing a domain service [Fact] public void TransferFunds_SufficientBalance_FundsTransferred() { // Arrange var sourceAccount = new BankAccount("123", 1000); var destinationAccount = new BankAccount("456", 0); var transferService = new FundTransferService(); // Act transferService.TransferFunds(sourceAccount, destinationAccount, 500); // Assert Assert.Equal(500, sourceAccount.Balance); Assert.Equal(500, destinationAccount.Balance); } // Incorrect: Testing a data access method instead of domain logic [Fact] public void CanRetrieveBankAccountFromDatabase() { // This tests infrastructure, not domain logic. Avoid. var bankAccount = _bankAccountRepository.GetById("123"); Assert.NotNull(bankAccount); } """ ### 1.2. Standard: Test-Driven Development (TDD) **Do This:** Use TDD to drive the design of domain objects and interactions. Write tests before writing the corresponding code. **Don't Do This:** Write code first and then write tests as an afterthought. **Why:** TDD leads to better-designed domain models because it forces you to think about the behavior of your domain objects before implementing them. It also ensures that your domain logic is always covered by tests. **Example:** """csharp // TDD Cycle Example // 1. Write a failing test based on a business requirement. [Fact] public void OrderTotal_MultipleItems_CalculatesCorrectly() { // Arrange var order = new Order(); order.AddItem(new OrderItem("Product1", 10, 2)); order.AddItem(new OrderItem("Product2", 5, 3)); // Act decimal total = order.Total(); // Assert Assert.Equal(35, total); // Test fails initially } // 2. Write the minimum amount of code to make the test pass. public class Order { private List<OrderItem> _items = new List<OrderItem>(); public decimal Total() { return _items.Sum(item => item.Price * item.Quantity); } public void AddItem(OrderItem item) { _items.Add(item); } } public class OrderItem { public string ProductName { get; set; } public decimal Price { get; set; } public int Quantity { get; set; } public OrderItem(string productName, decimal price, int quantity) { ProductName = productName; Price = price; Quantity = quantity; } } // 3. Refactor if necessary while ensuring tests continue to pass. """ ### 1.3. Standard: Behavior-Driven Development (BDD) **Do This:** Use BDD frameworks to specify the behavior of domain objects in a more expressive and human-readable way. Tools like SpecFlow or Cucumber can be used to write scenarios that describe how the system should behave. **Don't Do This:** Rely solely on unit tests without describing the overall behavior of the system in terms of scenarios and features. **Why:** BDD helps bridge the gap between technical and non-technical stakeholders by expressing acceptance criteria as executable specifications. This ensures everyone has a shared understanding of the system's behavior. **Example (SpecFlow):** """gherkin Feature: Order Management As a customer I want to be able to place an order So that I can receive the products I want Scenario: Place a valid order Given a customer with sufficient credit And a product is available in stock When the customer places an order for the product Then the order should be created successfully And the product quantity should be reduced And the customer's credit should be updated """ ## 2. Unit Testing ### 2.1. Standard: Isolate Domain Objects **Do This:** Use mocking or stubbing to isolate domain objects during unit testing. Avoid dependencies on external systems or infrastructure components. **Don't Do This:** Directly access databases, file systems, or other external resources during unit tests. **Why:** Unit tests should be fast, reliable, and repeatable. Dependencies on external resources can make tests slow, brittle, and difficult to maintain. **Example:** """csharp // Correct: Using Moq to mock a repository [Fact] public void PlaceOrder_ValidOrder_OrderPlacedSuccessfully() { // Arrange var mockRepository = new Mock<IOrderRepository>(); var orderService = new OrderService(mockRepository.Object); var order = new Order(); // Act orderService.PlaceOrder(order); // Assert mockRepository.Verify(repo => repo.Save(order), Times.Once); } // Incorrect: Directly accessing the database [Fact] public void PlaceOrder_ValidOrder_OrderPlacedSuccessfully_DatabaseAccess() { // This is BAD, avoid interacting with real database in UT // Arrange var orderService = new OrderService(new OrderRepository()); } """ ### 2.2. Standard: Testing Entities and Value Objects **Do This:** Write comprehensive tests for entities and value objects, focusing on invariant enforcement, business logic, and behavior. **Don't Do This:** Omit testing of entities and value objects, assuming they are simple data containers. **Why:** Entities and value objects encapsulate important business rules and data constraints. Proper testing ensures that these rules are enforced correctly and that the objects behave as expected. **Example:** """csharp // Testing a value object [Fact] public void Money_NegativeAmount_ThrowsException() { // Arrange, Act, Assert Assert.Throws<ArgumentException>(() => new Money(-100, "USD")); } // Testing an entity [Fact] public void AddItem_ValidItem_IncreasesItemCount() { // Arrange var order = new Order(); var item = new OrderItem("Product1", 10, 2); // Act order.AddItem(item); // Assert Assert.Equal(1, order.Items.Count); } """ ### 2.3. Standard: Testing Domain Services **Do This:** Write tests for domain services to verify that they orchestrate interactions between domain objects and implement complex business processes correctly. **Don't Do This:** Skip testing domain services or assume they are simply wrappers around other domain objects. **Why:** Domain services encapsulate important business logic that cannot be easily placed in entities or value objects. Proper testing ensures that these services behave as expected. **Example:** """csharp // Testing a domain service [Fact] public void TransferFunds_InsufficientBalance_ThrowsException() { // Arrange var sourceAccount = new BankAccount("123", 100); var destinationAccount = new BankAccount("456", 0); var transferService = new FundTransferService(); // Act & Assert Assert.Throws<InvalidOperationException>(() => transferService.TransferFunds(sourceAccount, destinationAccount, 500)); } """ ### 2.4. Standard: Using Fakes, Mocks, and Stubs **Do This:** Use fakes, mocks, and stubs appropriately to isolate the code under test. Use mocks for verifying interactions and stubs for providing fixed return values. **Don't Do This:** Overuse mocking or stubbing, leading to tests that are tightly coupled to the implementation details. **Why:** Proper use of test doubles ensures that tests are focused on the behavior of the code under test and not on the behavior of its dependencies. **Example:** """csharp // Using a mock to verify an interaction [Fact] public void SendEmail_OrderPlaced_EmailSent() { // Arrange var mockEmailService = new Mock<IEmailService>(); var orderService = new OrderService(null, mockEmailService.Object); var order = new Order(); // Act orderService.PlaceOrder(order); // Assert mockEmailService.Verify(email => email.Send(It.IsAny<string>(), It.IsAny<string>()), Times.Once); } // Using a stub to provide a fixed return value [Fact] public void GetDiscount_CustomerEligible_ReturnsDiscount() { // Arrange var stubCustomerRepository = new StubCustomerRepository(); stubCustomerRepository.SetCustomerEligibility(true); var discountService = new DiscountService(stubCustomerRepository); var customerId = "123"; // Act decimal discount = discountService.GetDiscount(customerId); // Assert Assert.Equal(0.1m, discount); // Assuming 10% discount } public class StubCustomerRepository : ICustomerRepository { private bool _isEligible; public void SetCustomerEligibility(bool isEligible) { _isEligible = isEligible; } public Customer GetById(string id) { return new Customer { IsEligible = _isEligible }; } } """ ## 3. Integration Testing ### 3.1. Standard: Verify Interactions Between Bounded Contexts **Do This:** Write integration tests to verify interactions between bounded contexts. Use messaging or APIs to simulate real-world communication. **Don't Do This:** Skip integration testing or assume that interactions between bounded contexts will work seamlessly. **Why:** Bounded contexts often have complex interactions, especially in distributed systems. Integration tests ensure that these interactions are correctly implemented and that data is consistently shared between contexts. **Example:** """csharp // Integration test between two services using a message bus [Fact] public async Task OrderService_PlacesOrder_PublishesOrderCreatedEvent() { // Arrange var messageBus = new InMemoryMessageBus(); var orderService = new OrderService(null, null, messageBus); var order = new Order(); // Act await orderService.PlaceOrder(order); // Assert var orderCreatedEvent = messageBus.PublishedEvents.OfType<OrderCreatedEvent>().FirstOrDefault(); Assert.NotNull(orderCreatedEvent); Assert.Equal(order.Id, orderCreatedEvent.OrderId); } """ ### 3.2. Standard: Test Aggregate Consistency **Do This:** Create integration tests that focus on maintaining consistency between aggregates. These tests should simulate real-world scenarios involving multiple aggregates. **Don't Do This:** Unit test aggregates in isolation and assume that consistency will be maintained during complex operations. **Why:** Aggregates are units of consistency in DDD. Integration tests ensure that consistency is maintained across multiple aggregates. **Example:** """csharp // Integration test involving two aggregates [Fact] public void ShipOrder_ValidOrder_UpdatesInventoryAndOrderStatus() { // Arrange var order = new Order { Id = Guid.NewGuid(), Status = OrderStatus.Placed }; var product = new Product { Id = Guid.NewGuid(), Inventory = 10 }; // Simulate the order being placed order.OrderItems = new List<OrderItem> { new OrderItem(product.Id.ToString(), "Product1", 10, 1) }; var orderRepository = new InMemoryOrderRepository(); orderRepository.Save(order); var productRepository = new InMemoryProductRepository(); productRepository.Save(product); var shippingService = new ShippingService(orderRepository, productRepository); // Act shippingService.ShipOrder(order.Id); // Assert var updatedOrder = orderRepository.GetById(order.Id.ToString()); Assert.Equal(OrderStatus.Shipped, updatedOrder.Status); var updatedProduct = productRepository.GetById(product.Id.ToString()); Assert.Equal(9, updatedProduct.Inventory); } """ ### 3.3. Standard: Database Integration Testing **Do This:** Use database integration tests to verify that data is correctly persisted and retrieved from the database. Use test databases to avoid affecting production data. **Don't Do This:** Skip database integration tests or assume that database interactions will work correctly based on ORM configurations alone. **Why:** Database interactions are a common source of errors. Integration tests ensure that data mappings and database queries are correctly implemented. **Example:** """csharp // Database integration test using Entity Framework Core [Fact] public void AddCustomer_ValidCustomer_CustomerPersistedToDatabase() { // Arrange var options = new DbContextOptionsBuilder<MyDbContext>() .UseInMemoryDatabase(databaseName: "TestDatabase") .Options; using (var context = new MyDbContext(options)) { var customer = new Customer { Id = Guid.NewGuid(), Name = "John Doe" }; // Act context.Customers.Add(customer); context.SaveChanges(); } // Assert using (var context = new MyDbContext(options)) { var persistedCustomer = context.Customers.FirstOrDefault(c => c.Name == "John Doe"); Assert.NotNull(persistedCustomer); Assert.Equal("John Doe", persistedCustomer.Name); } } """ ## 4. End-to-End Testing ### 4.1. Standard: Simulate User Scenarios **Do This:** Write end-to-end tests that simulate real user scenarios, covering multiple layers of the application, including the UI, application services, and domain model.. **Don't Do This:** Focus solely on unit or integration tests without verifying that the entire system works together as expected. **Why:** End-to-end tests ensure that the system meets the overall business requirements and that all components work together correctly. **Example:** """csharp // End-to-end test using Selenium [Fact] public void PlaceOrder_ValidOrder_OrderConfirmationDisplayed() { // Arrange using (var driver = new ChromeDriver()) { driver.Navigate().GoToUrl("http://localhost:5000/Order/New"); // Act driver.FindElement(By.Id("ProductName")).SendKeys("Product1"); driver.FindElement(By.Id("Quantity")).SendKeys("2"); driver.FindElement(By.Id("PlaceOrderButton")).Click(); // Assert Assert.Contains("Order Confirmation", driver.PageSource); } } // This example assumes a web application running locally on port 5000 """ ### 4.2. Standard: Test User Interface Interactions **Do This:** Use tools like Selenium or Playwright to automate user interface interactions and verify that the UI behaves as expected. **Don't Do This:** Manually test the UI or assume that UI interactions will work correctly based on unit tests alone. **Why:** UI interactions are a common source of errors. Automated UI tests ensure that the UI behaves correctly and that user workflows are working as expected. **Example:** """csharp // End-to-end test using Playwright [Fact] public async Task PlaceOrder_ValidOrder_OrderConfirmationDisplayed_Playwright() { using var playwright = await Playwright.CreateAsync(); await using var browser = await playwright.Chromium.LaunchAsync(); var page = await browser.NewPageAsync(); await page.GotoAsync("http://localhost:5000/Order/New"); await page.FillAsync("#ProductName", "Product1"); await page.FillAsync("#Quantity", "2"); await page.ClickAsync("#PlaceOrderButton"); await page.WaitForSelectorAsync("text=Order Confirmation"); var content = await page.ContentAsync(); Assert.Contains("Order Confirmation", content); } """ ### 4.3. Standard: Test Cross-Cutting Concerns **Do This:** Include tests for cross-cutting concerns like security, performance, and logging in your end-to-end tests. **Don't Do This:** Focus solely on functional requirements without considering cross-cutting concerns. **Why:** Cross-cutting concerns can have a significant impact on the overall quality and reliability of the system. End-to-end tests ensure that these concerns are addressed correctly. **Example:** """csharp // End-to-end test for security (authorization) [Fact] public void AccessAdminPage_UnauthorizedUser_RedirectedToLoginPage() { // Arrange using (var driver = new ChromeDriver()) { // Act driver.Navigate().GoToUrl("http://localhost:5000/Admin/Dashboard"); // Assert Assert.Contains("Login", driver.PageSource); // Assuming a login page } } """ ## 5. Test Data Management ### 5.1. Standard: Use Test Data Builders **Do This:** Use test data builders to create test data in a consistent and maintainable way. **Don't Do This:** Create test data directly in the test methods or hardcode values. **Why:** Test data builders make it easier to create test data and ensure that the data is consistent across tests. **Example:** """csharp // Test data builder public class OrderBuilder { private Order _order = new Order(); public OrderBuilder WithId(Guid id) { _order.Id = id; return this; } public OrderBuilder WithItem(string productName, decimal price, int quantity) { _order.AddItem(new OrderItem(productName, price, quantity)); return this; } public Order Build() { return _order; } } // Using the test data builder in a test [Fact] public void OrderTotal_MultipleItems_CalculatesCorrectly_UsingBuilder() { // Arrange var order = new OrderBuilder() .WithItem("Product1", 10, 2) .WithItem("Product2", 5, 3) .Build(); // Act decimal total = order.Total(); // Assert Assert.Equal(35, total); } """ ### 5.2. Standard: Clean Up Test Data **Do This:** Ensure that test data is cleaned up after each test to avoid affecting subsequent tests. **Don't Do This:** Leave test data in the database or other external resources after the tests are complete. **Why:** Cleaning up test data ensures that tests are repeatable and that the test environment remains in a consistent state. **Example:** """csharp // Cleaning up test data using xUnit's IDisposable public class MyTestClass : IDisposable { private readonly MyDbContext _context; public MyTestClass() { var options = new DbContextOptionsBuilder<MyDbContext>() .UseInMemoryDatabase(databaseName: "TestDatabase") .Options; _context = new MyDbContext(options); _context.Database.EnsureCreated(); } [Fact] public void MyTest() { // Your test logic here } public void Dispose() { _context.Database.EnsureDeleted(); _context.Dispose(); } } """ ## 6. Performance Testing in DDD ### 6.1. Standard: Profile Domain Logic **Do This:** Profile domain logic to identify performance bottlenecks. Ensure that performance tests reflect real-world scenarios and data volumes. **Don't Do This:** Ignore performance testing or assume that domain logic is inherently fast. **Why:** Performance bottlenecks in domain logic can significantly impact the overall performance of the system. Profiling helps identify and address these bottlenecks. **Example:** """csharp // Performance test using BenchmarkDotNet [MemoryDiagnoser] public class OrderTotalBenchmark { private Order _order; [GlobalSetup] public void Setup() { _order = new OrderBuilder() .WithItem("Product1", 10, 2) .WithItem("Product2", 5, 3) .Build(); } [Benchmark] public decimal CalculateOrderTotal() { return _order.Total(); } } """ ### 6.2. Standard: Optimize Database Queries **Do This:** Optimize database queries to minimize the number of round trips to the database and the amount of data transferred. **Don'tDo This:** Use inefficient queries that retrieve unnecessary data or perform poorly. **Why:** Database queries are often a major source of performance bottlenecks """csharp // Optimizing database queries using Include and AsNoTracking [Fact] public void GetOrder_WithItems_RetrievesOrderAndItemsEfficiently() { // Arrange - set up some pre-existing Orders and Items // Act using (var context = new AppDbContext()) { var order = context.Orders .Include(o => o.Items) // Eagerly load the order items .AsNoTracking() // Disable change tracking .FirstOrDefault(o => o.Id == 123); // Do assertions here } } """ ### 6.3 Standard: Implement Caching Strategies **Do This:** Implement caching strategies to reduce the load on the database and improve response times. Use distributed caching for scalable applications. **Don't Do This:** Overuse caching or cache data that is frequently changing. **Why:** Caching can significantly improve the performance of read-heavy applications.. ## 7. Security Testing in DDD ### 7.1. Standard: Test Authorization and Authentication **Do This:** Write tests to verify that authorization and authentication are correctly implemented and that users can only access resources they are authorized to access. **Don't Do This:** Skip security testing or assume that authorization and authentication are correctly implemented based on framework configurations alone. **Why:** Security vulnerabilities can have a significant impact on the overall security of the system. **Example:** """csharp // Security test for authorization [Fact] public void AccessAdminPage_UnauthorizedUser_RedirectedToLoginPage() { // Arrange using (var client = new TestServer(new WebHostBuilder().UseStartup<Startup>()).CreateClient()) { // Act var response = await client.GetAsync("/Admin/Dashboard"); // Assert Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); } } """ ### 7.2. Standard: Test Input Validation **Do This:** Write tests to verify that input validation is correctly implemented and that the system is protected against malicious input. **Don't Do This:** Skip input validation testing or assume that input validation is correctly implemented based on framework configurations alone. **Why:** Input validation is a critical security measure that helps prevent attacks like SQL injection and cross-site scripting (XSS). """csharp // Security test for input validation [Fact] public void CreateUser_InvalidEmail_ReturnsBadRequest() { // Arrange, Act, Assert var ex = Assert.Throws<ArgumentException>( () => new User("", "bademail", "password" )); Assert.Contains("Email", ex.Message); } """ ### 7.3. Standard: Sensitive Data Handling **Do This:** Conduct tests focusing on the secure handling of sensitive data, including encryption techniques and secure storage practices. **Don't Do This:** Store sensitive data in plain text or use weak encryption algorithms, and avoid conducting regular security audits. **Why:** Prevent unauthorized access and maintain data integrity. ## 8. Continuous Integration and Continuous Delivery (CI/CD) ### 8.1. Standard: Automate Testing **Do This:** Automate all tests as part of the CI/CD pipeline. Ensure that tests are run automatically on every commit. **Don't Do This:** Manually run tests or skip testing in the CI/CD pipeline. **Why:** Automated testing ensures that code changes are continuously validated and that defects are detected early. ### 8.2. Standard: Monitor Test Results **Do This:** Monitor test results and track test coverage. Use test dashboards to visualize test results and identify trends. **Don't Do This:** Ignore test results or assume that tests are always passing. **Why:** Monitoring test results and test coverage helps identify areas of the code that are not well-tested and track the overall quality of the system. ## 9. Anti-Patterns in DDD Testing ### 9.1. Data Access Logic in Entities **Avoid:** Placing database access logic directly inside entities. **Why:** This couples the domain model to a specific infrastructure concern, making it harder to test and maintain. ### 9.2. Anemic Domain Model **Avoid:** Creating an anemic domain model with entities that only contain data and no behavior. **Why:** This moves business logic out of the domain model and into application services, leading to a less maintainable and less testable system. ### 9.3. God Classes/Services **Avoid:** Creating God classes or services that encapsulate too much logic. **Why:** This makes the code harder to understand, test, and maintain. ### 9.4. Over-Mocking **Avoid:** Over-mocking dependencies, leading to tests that are brittle and tightly coupled to the implementation details. **Why:** Over-mocking can make tests less effective by masking real issues and making them less resilient to code changes. By following these coding standards, development teams can create robust and well-tested domain models that are aligned with the principles of DDD. This leads to more maintainable, performant, and secure software.