# How IUnitOfWork Manages the Lifecycle of Domain Objects in DDDplus

> Discover how DDDplus IUnitOfWork manages domain object lifecycles atomically across aggregates within a single transaction. Learn to transition objects from transient to persistent state seamlessly.

- Repository: [Funky Gao/cp-ddd-framework](https://github.com/funkygao/cp-ddd-framework)
- Tags: how-to-guide
- Published: 2026-03-02

---

**In DDDplus, `IUnitOfWork` acts as an Application Layer coordination hub that atomically persists aggregates across different roots, ensuring domain objects transition from transient to persistent state within a single transactional boundary.**

The `funkygao/cp-ddd-framework` repository implements the Unit of Work pattern to enforce strict separation between domain logic and infrastructure concerns. By defining a clear semantic contract through the `IUnitOfWork` interface, the framework enables application services to coordinate complex persistence operations spanning multiple aggregate roots while remaining agnostic to transaction management details.

## The Marker Interface Pattern

At its core, `IUnitOfWork` is a **marker interface** that declares participation in the unit-of-work pattern without imposing implementation constraints. Defined in [`dddplus-spec/src/main/java/io/github/dddplus/model/IUnitOfWork.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-spec/src/main/java/io/github/dddplus/model/IUnitOfWork.java), this empty interface serves as a semantic signal: any class implementing it commits to coordinating persistence operations atomically.

This design allows the domain layer to remain pure while the application layer handles cross-cutting infrastructure concerns. The interface itself contains no methods, relying on concrete implementations to provide specific `persist(..)` overloads for different aggregate combinations.

## Cross-Aggregate Transaction Coordination

Real-world business use cases frequently touch multiple aggregate roots simultaneously—such as a `Task`, `Carton`, and `Order` in warehouse management scenarios. `IUnitOfWork` establishes a **transaction boundary** that guarantees atomicity across these distinct aggregates, preventing partial persistence failures that would violate domain invariants.

The concrete implementation in [`dddplus-test/src/test/java/ddd/plus/showcase/wms/application/UnitOfWork.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-test/src/test/java/ddd/plus/showcase/wms/application/UnitOfWork.java) demonstrates this coordination:

```java
@Component
@Setter(onMethod_ = {@Autowired})
@Slf4j
public class UnitOfWork implements IUnitOfWork {
    private ITaskRepository taskRepository;
    private IOrderRepository orderRepository;
    private ICartonRepository cartonRepository;
    private IShipRepository shipRepository;

    @Transactional(rollbackFor = Exception.class)
    public void persist(@NonNull Task task, @NonNull Carton carton) {
        taskRepository.save(task);   // persists Task aggregate
        cartonRepository.save(carton); // persists Carton aggregate
    }
}

```

Each `persist(..)` method variant handles specific aggregate combinations, ensuring that all participating repositories—including `ITaskRepository` and `ICartonRepository`—commit or rollback together under Spring's transaction management.

## Spring-Managed Transaction Lifecycle

The `UnitOfWork` implementation is a Spring-managed `@Component` that delegates low-level transaction handling to the container. Every persistence method carries the `@Transactional(rollbackFor = Exception.class)` annotation, which instructs Spring to:

- Open a database transaction upon method entry
- Execute repository operations such as `save`, `insert`, or `switchToCanceledStatus`
- Automatically commit on successful return or rollback on exception propagation

This infrastructure abstraction keeps domain logic free from `EntityManager` or JDBC transaction APIs. Application services inject the `IUnitOfWork` interface and invoke persistence operations without managing transaction boundaries manually.

## Lifecycle State Transitions

Within each transactional scope, `UnitOfWork` orchestrates the transition of aggregates from **new or modified states** to **persistent states**. The implementation forwards calls to repository interfaces—such as `ICartonRepository` defined in [`dddplus-test/src/test/java/ddd/plus/showcase/wms/domain/carton/ICartonRepository.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-test/src/test/java/ddd/plus/showcase/wms/domain/carton/ICartonRepository.java)—which materialize the aggregate state into database tables.

Because repository operations execute within the transaction initiated by `UnitOfWork`, aggregates maintain consistency even when persistence spans multiple physical tables or schema boundaries. The unit of work tracks which aggregates require persistence and coordinates their transition to persistent identity together.

## Idempotence and Guard Checks

Before delegating to repositories, specific `persist` overloads enforce **domain invariants** and idempotence requirements. For example, when handling `Task` and `OrderBagCanceled` combinations, the implementation invokes `uuid.assureVaryOnce()` to prevent duplicate processing of the same business event.

These guard checks execute within the transactional boundary but strictly before any repository interaction. This sequencing ensures that only valid state changes reach the database, while maintaining atomicity between validation and persistence.

## Practical Implementation in Application Services

Application services consume `IUnitOfWork` through constructor injection, casting to the concrete implementation to access type-specific `persist` methods:

```java
@Component
@RequiredArgsConstructor
public class ShipOrderUseCase {
    private final IUnitOfWork unitOfWork;      // injected concrete UnitOfWork
    private final IOrderRepository orderRepo;
    private final IShipRepository shipRepo;

    @Transactional
    public void execute(@NonNull Order order, @NonNull ShipManifest manifest) {
        // business validation omitted for brevity
        ((UnitOfWork) unitOfWork).persist(manifest, order);
        // No explicit commit needed – Spring commits at method exit
    }
}

```

This pattern allows services to remain testable through the `IUnitOfWork` interface while leveraging the specific coordination logic implemented in `UnitOfWork`.

## Summary

- **IUnitOfWork** is a marker interface in `dddplus-spec` that defines the semantic contract for unit-of-work participation without implementation coupling.
- The concrete **UnitOfWork** class in the application layer coordinates persistence across multiple aggregate roots (e.g., `Task`, `Carton`, `Order`) within a single atomic transaction.
- **Spring's `@Transactional`** annotation on `persist(..)` methods provides automatic commit/rollback behavior, separating transaction infrastructure from domain logic.
- Repository interfaces such as `ITaskRepository` execute within this boundary to transition aggregates from transient to persistent states consistently.
- **Guard checks** like `uuid.assureVaryOnce()` enforce idempotence and domain invariants before database interaction occurs.

## Frequently Asked Questions

### What distinguishes IUnitOfWork from the concrete UnitOfWork implementation?

`IUnitOfWork` is a marker interface located in [`dddplus-spec/src/main/java/io/github/dddplus/model/IUnitOfWork.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-spec/src/main/java/io/github/dddplus/model/IUnitOfWork.java) that establishes the semantic contract for unit-of-work participation. The concrete `UnitOfWork` class implements this interface and provides Spring-managed transactional coordination, located in [`dddplus-test/src/test/java/ddd/plus/showcase/wms/application/UnitOfWork.java`](https://github.com/funkygao/cp-ddd-framework/blob/main/dddplus-test/src/test/java/ddd/plus/showcase/wms/application/UnitOfWork.java).

### How does IUnitOfWork ensure atomicity across multiple aggregates?

The concrete `UnitOfWork` implementation uses Spring's `@Transactional(rollbackFor = Exception.class)` annotation on its `persist(..)` methods. When an application service invokes these methods, Spring opens a transaction that encompasses all repository calls (such as `taskRepository.save()` and `cartonRepository.save()`), ensuring they commit or rollback as a single unit.

### Can IUnitOfWork manage the lifecycle of a single aggregate?

While `IUnitOfWork` excels at coordinating multiple aggregates, it supports single-aggregate persistence through specific method overloads. However, direct repository usage is typically preferred for single-aggregate operations unless the additional guard checks and idempotence validation provided by `UnitOfWork` are required.

### Where should domain validation occur in the persistence lifecycle?

Guard checks belong in the concrete `UnitOfWork` implementation before repository delegation. According to the DDDplus source code, validations such as `uuid.assureVaryOnce()` execute within the transactional scope but prior to any repository save operations, ensuring invalid states never reach the database while maintaining atomicity with persistence.