# Sharing IDs Across Aggregates

In the world of Domain-Driven Design (DDD), certain principles often feel like ironclad rules—one of which is that every aggregate must have a unique identity. But what if I told you that **sharing IDs across aggregates isn’t inherently wrong** and, in fact, can be a practical, scalable approach in many domains?

## The Controversial Question

When designing a system with multiple related aggregates—say, a `CustomerAggregate`, `OrderAggregate`, and `CustomerPreferencesAggregate`—the instinct is to assign each a unique ID. After all, isn’t each aggregate supposed to be an independent consistency boundary?

But what if these aggregates share the same ID, such as a customer ID? Would this violate the sacred principles of DDD, or could it be a pragmatic decision?

## Designing Aggregates: Consistency Boundaries, Not Relationships

A common misconception when designing aggregates is to organize them around **relationships, hierarchy, or real-world manifestations** rather than **consistency boundaries**. Aggregates are about managing invariants—rules that must always hold true—not about representing relational data structures.

For example, consider a retail domain:

* An `OrderAggregate` enforces consistency around orders—items, payment status, and shipping details.
    
* A `CustomerAggregate` manages identity and personal details, but not orders or preferences.
    
* A `CustomerPreferencesAggregate` handles marketing subscriptions and notifications.
    

Designing aggregates based on relationships often results in bloated objects and inconsistent states. Instead, aggregates should isolate rules and invariants, ensuring consistency within their boundary.

## Why Sharing IDs Can Be OK

Using the same ID across multiple aggregates is acceptable—**if done correctly**:

### ✅ 1. Distinct Responsibilities

When aggregates encapsulate **separate, independent behaviors**, sharing an ID can make sense. For instance:

* `CustomerAggregate` manages identity and personal information.
    
* `OrderAggregate` handles orders and purchases.
    
* `CustomerPreferencesAggregate` manages notification and marketing preferences.
    

If each aggregate enforces its own rules, they can coexist peacefully, even with the same ID.

### ✅ 2. Clear Consistency Boundaries

When consistency boundaries are **truly independent**, you can safely use shared IDs. For example, changes to `CustomerPreferencesAggregate` should never require an immediate update to `OrderAggregate`.

### ✅ 3. Identity vs. State Management

An ID can represent a **common identity** without implying ownership. Each aggregate manages its own data and state, avoiding tight coupling.

## Expanded Example: A Food Delivery Domain

Let’s consider a food delivery service with three aggregates:

1. `CustomerAggregate` tracks user identity and contact details.
    
2. `OrderAggregate` handles order details, payment status, and delivery logistics.
    
3. `LoyaltyPointsAggregate` manages customer rewards.
    

```javascript
// CustomerAggregate.js
class CustomerAggregate {
  constructor(customerId, name, address) {
    this.customerId = customerId;
    this.name = name;
    this.address = address;
  }

  updateAddress(newAddress) {
    this.address = newAddress;
  }
}
```

```javascript
// OrderAggregate.js
class OrderAggregate {
  constructor(customerId, orderId, items) {
    this.customerId = customerId;
    this.orderId = orderId;
    this.items = items;
    this.status = "Pending";
  }

  markAsDelivered() {
    this.status = "Delivered";
  }
}
```

```javascript
// LoyaltyPointsAggregate.js
class LoyaltyPointsAggregate {
  constructor(customerId) {
    this.customerId = customerId;
    this.points = 0;
  }

  addPoints(points) {
    this.points += points;
  }
}
```

This approach aligns aggregates with consistency boundaries—each handles its own invariants. We can use the customer ID across all aggregates because their state and behavior remain independent.

## When It’s a Problem

Sharing IDs becomes problematic if:

* Aggregates frequently rely on each other’s state.
    
* Consistency across aggregates is required in a single transaction.
    
* You’re modeling the same entity in different places.
    

If these conditions exist, consider merging aggregates or using domain events to ensure consistency.

## Final Thoughts

In DDD, aggregates are about managing business rules, not reflecting real-world hierarchies. Sharing IDs isn’t heretical—it’s a pragmatic choice when aggregates have distinct responsibilities and consistency boundaries. Avoid dogmatic thinking; focus on designing aggregates around consistency, invariants, and behaviors. The goal is a **model that fits the business problem**, not one that rigidly follows patterns. Embrace trade-offs; DDD is about practical solutions, not absolute rules.

And remember—**It's acceptable to share IDs across different aggregate types, but an individual aggregate instance must always have a unique identity within its own type.** This ensures each instance maintains a unique identity and consistency boundary, reinforcing the core principles of DDD while remaining pragmatic.
