【问题标题】:Domain entities using Hibernate entities?使用 Hibernate 实体的域实体?
【发布时间】:2020-01-17 17:11:08
【问题描述】:

在我目前在 Spring Boot 应用程序中与 DDD 的斗争中,我陷入了僵局。我知道我的域实体不应该与基础设施层(休眠实体所在的位置)有任何联系。当然,我的领域层依赖于 Hibernate 实体的数据来完成它的操作。

所以在我的应用层中,我的服务加载 Hibernate 实体并在域实体中传递它。

这是我的域实体的示例:

package com.transportifygame.core.domain.objects;

import com.transportifygame.core.domain.OperationResult;
import com.transportifygame.core.domain.constants.Garages;
import com.transportifygame.core.domain.exceptions.garages.NotAvailableSpotException;
import com.transportifygame.core.infrastructure.entities.CompanyEntity;
import com.transportifygame.core.infrastructure.entities.GarageEntity;
import com.transportifygame.core.infrastructure.entities.LocationEntity;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;

@Getter
@Setter
@RequiredArgsConstructor
@AllArgsConstructor

public class Garage {

    private GarageEntity garage;

    public static Integer getAvailableSlots(Garages.Size size) {
        switch (size) {
            case SMALL:
                return Garages.Slots.SMALL;
            case MEDIUM:
                return Garages.Slots.MEDIUM;
            case LARGE:
                return Garages.Slots.LARGE;
            case HUGE:
                return Garages.Slots.HUGE;
        }

        return 0;
    }

    public static Double getGaragePriceToBuy(Garages.Size size) {
        switch (size) {
            case SMALL:
                return Garages.Prices.BUY_SMALL;
            case MEDIUM:
                return Garages.Prices.BUY_MEDIUM;
            case LARGE:
                return Garages.Prices.BUY_LARGE;
            case HUGE:
                return Garages.Prices.BUY_HUGE;
        }

        return 0.0;
    }

    public static OperationResult<GarageEntity, Object> buy(
        Garages.Size size,
        CompanyEntity company,
        LocationEntity location
    ) {
        // As we had changed the company object, we have to refresh
        var newGarage = new GarageEntity();
        newGarage.setCompany(company);
        newGarage.setLocation(location);
        newGarage.setSize(size.ordinal());
        newGarage.setSlotsAvailable(Garage.getAvailableSlots(size));

        return new OperationResult<>(newGarage, null);
    }

    public static void hasAvailableSpot(GarageEntity garage) throws NotAvailableSpotException {
        if (garage.getSlotsAvailable() == 0) {
            throw new NotAvailableSpotException();
        }
    }

    public static OperationResult<GarageEntity, Object> addFreeSlot(GarageEntity garage) {
        garage.setSlotsAvailable(garage.getSlotsAvailable() - 1);

        return new OperationResult<>(garage, null);
    }

    public static OperationResult<GarageEntity, Object> removeFreeSlot(GarageEntity garage) {
        garage.setSlotsAvailable(garage.getSlotsAvailable() + 1);

        return new OperationResult<>(garage, null);
    }

}

现在的问题是,这是为域实体提供所需数据的正确方法吗?如果没有,正确的方法是什么?

是否使用工厂来构建基于休眠实体的域实体?域实体应该镜像休眠实体属性?

我也看过有人用这种方式在Hibernate实体中添加域实体的逻辑,但我认为这不是正确的做法。

【问题讨论】:

    标签: java spring hibernate domain-driven-design


    【解决方案1】:

    我建议您熟悉 DDD 存储库模式。简而言之,存储库应该模仿内存中的集合来检索和存储您的域对象。

    因此,您最终可以通过两种方式使用 hibernate 设计您的应用程序:

    1. 编写一个高级存储库接口,为您的域实体公开“查找”、“保存”等。存储库接口和返回的实体应该是“纯域逻辑”。也就是说:没有持久性或休眠问题。在您的情况下,它将是 Garage 对象,其中实现了所有业务方法。 在存储库实现(可能位于另一个层/包中)中,您将处理休眠行表示(休眠实体)和实体管理器以完成接口的合同。在您的情况下,这是Garage 对象映射到(哑)GarageEntity 并通过EntityManagerSession 持久化的地方。

    2. 您可以接受一些持久性/休眠泄漏到您的域中。这将导致拥有一个 Garage 域对象,其中包含同一源文件上的所有业务逻辑和 hibernate/jpa 注释(或 xml)(xml 可以是单独的)。在这种情况下,存储库可能是直接的 jpa 存储库实现。

    在任何情况下,DDD 中的逻辑都是一样的:

    在应用程序服务(一些GarageService - 用例的入口点)中查询存储库以获取对其执行业务操作的域对象。然后应用服务将修改后的实体再次存储在存储库中。

    【讨论】:

    • 我自己也一直在思考同样的问题,恕我直言@winson 的回答很准确。我要补充一点,有时从一个有点脏的混合域/jpa-entity 实现开始会更容易,一旦域足够稳定,从域实体中重构 jpa-entity。
    • 谢谢。这就是为什么我现在提倡反对休眠。我认为干净的架构只在解决方案 1 中给出,如果我必须将域逻辑映射到持久性逻辑,我不妨直接映射到表。所以我放弃了休眠,转而使用更接近 sql 的库。话虽如此,您可以在解决方案 2 中走得更远,而不会遇到任何问题。这个解决方案 - 这很重要 - 在短期内节省了一些开发时间。
    猜你喜欢
    • 1970-01-01
    • 2010-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-09
    • 2013-05-11
    • 2019-05-25
    • 2018-12-10
    相关资源
    最近更新 更多