Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Aggregate to JPA Entity mapping

In a DDD-project I'm contributing to, we're seeking for some convenient solutions to map entity objects to domain objects and visa versa.

Developers of this project agreed to fully decouple domain model from data model. The data layer uses JPA (Hibernate) as persistence technology.

As we all reckon that persistence is an implementation detail in DDD, from a developers' point of view we're all seeking for the most appropriate solution in every aspect of the application.

The biggest concern we're having is when an aggregate, containing a list of entities, is mapped to a JPA entity that in it's turn contains a one-to-many relationship.

Take a look at the example below:

Domain model

public class Product extends Aggregate {
    private ProductId productId;
    private Set<ProductBacklogItem> backlogItems;

    // constructor & methods omitted for brevity
}

public class ProductBacklogItem extends DomainEntity {
    private BacklogItemId backlogItemId;
    private int ordering;
    private ProductId productId;

    // constructor & methods omitted for brevity
}

Data model

public class ProductJpaEntity {
    private String productId;
    @OneToMany
    private Set<ProductBacklogItemJpaEntity> backlogItems;

    // constructor & methods omitted for brevity
}

public class ProductBacklogItemJpaEntity {
    private String backlogItemId;
    private int ordering;
    private String productId;

    // constructor & methods omitted for brevity
}

Repository

public interface ProductRepository {        
    Product findBy(ProductId productId);
    void save(Product product);
}

class ProductJpaRepository implements ProductRepository {        
    @Override
    public Product findBy(ProductId productId) {
        ProductJpaEntity entity = // lookup entity by productId

        ProductBacklogItemJpaEntity backlogItemEntities = entity.getBacklogItemEntities();        
        Set<ProductBacklogItem> backlogItems = toBackLogItems(backlogItemEntities);

        return new Product(new ProductId(entity.getProductId()), backlogItems);
    }

    @Override
    public void save(Product product) {
        ProductJpaEntity entity = // lookup entity by productId

        if (entity == null) {
          // map Product and ProductBacklogItems to their corresponding entities and save
          return;
        }

        Set<ProductBacklogItem> backlogItems = product.getProductBacklogItems();
        // how do we know which backlogItems are: new, deleted or adapted...?
    }
}

When a ProductJpaEntity already exists in DB, we need to update everything. In case of an update, ProductJpaEntity is already available in Hibernate PersistenceContext. However, we need to figure out which ProductBacklogItems are changed.

More specifically:

  • ProductBacklogItem could have been added to the Collection
  • ProductBacklogItem could have been removed from the Collection

Each ProductBacklogItemJpaEntity has a Primary Key pointing to the ProductJpaEntity. It seems that the only way to detect new or removed ProductBacklogItems is to match them by Primary Key. However, primary keys don't belong in the domain model...

There's also the possibility to first remove all ProductBacklogItemJpaEntity instances (which are present in DB) of a ProductJpaEntity, flush to DB, create new ProductBacklogItemJpaEntity instances and save them to DB. This would be a bad solution. Every save of a Product would lead to several delete and insert statements in DB.

Which solution exists to solve this problem without making too many sacrifices on Domain & Data model?

like image 1000
user2054927 Avatar asked Nov 16 '22 12:11

user2054927


1 Answers

You can let JPA/Hibernate solve problem for you.

public void save(Product product) {
    ProductJpaEntity entity = convertToJpa(product);
    entityManager.merge(entity);
    // I think that actually save(entity) would call merge for you,
    // if it notices that this entity already exists in database
}

What this will do is:

  • It will take your newly created JPA Entity and attach it
  • It will examine what is in database and update all relations accordingly, with priority given to your created entity (if mappings are set correctly)
like image 166
Dejan Pekter Avatar answered Jan 04 '23 02:01

Dejan Pekter