首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >请解释仓库-、映射-和业务层的关系和责任

请解释仓库-、映射-和业务层的关系和责任
EN

Stack Overflow用户
提问于 2016-08-24 16:37:18
回答 2查看 1.6K关注 0票数 10

我读了很多关于这个东西的文章,现在我正在开发一个更大的web应用程序及其相应的后端。

但是,我从一个设计开始,在这个设计中,我要求存储库从数据库中获取数据并将其映射到DTO中。为什么是DTO?因为到目前为止,基本上所有的事情都是简单的,不需要更多的复杂性。如果它变得更加复杂,那么我就开始在服务层直接映射1到n之间的关系。类似于:

代码语言:javascript
复制
// This is Service-Layer
public List<CarDTO> getCarsFromOwner(Long carOwnerId) {

    // Entering Repository-Layer
    List<CarDTO> cars = this.carRepository = this.carRepository.getCars(carOwnerId);
    Map<Long, List<WheelDTO>> wheelMap = this.wheelRepository.getWheels(carId);

    for(CarDTO car : cars) {
        List<WheelDTO> wheels = wheelMap.get(car.getId());
        car.setWheels(wheels);
    }

    return cars;
}

当然,这是可行的,但事实证明,有时候事情比这更复杂,我开始意识到,如果我不对此做任何事情,代码看起来可能会很难看。

当然,我可以在wheelMap中加载CarRepository,在那里执行轮询映射,并且只返回完整的对象,但是由于SQL查询有时看起来非常复杂,所以我不想获取所有的cars及其wheels,还要处理getCars(Long ownerId)中的映射。

我显然错过了一个商业层,对吧?但我根本无法理解它的最佳实践。

假设我有CarOwner业务对象。我的代码会像这样:

代码语言:javascript
复制
// This is Service-Layer
public List<CarDTO> getCarsFromOwner(Long carOwnerId) {

    // The new Business-Layer
    CarOwner carOwner = new CarOwner(carOwnerId);
    List<Car> cars = carOwner.getAllCars();

    return cars;
}

这看起来很简单,但是内部会发生什么呢?这个问题特别针对CarOwner#getAllCars()

我设想这个函数将使用Mappers和存储库来加载数据,特别是处理关系映射部分:

代码语言:javascript
复制
List<CarDTO> cars = this.carRepository = this.carRepository.getCars(carOwnerId);
Map<Long, List<WheelDTO>> wheelMap = this.wheelRepository.getWheels(carId);

for(CarDTO car : cars) {
    List<WheelDTO> wheels = wheelMap.get(car.getId());
    car.setWheels(wheels);
}

但是怎么做呢?CarMapper是否提供getAllCarsWithWheels()getAllCarsWithoutWheels()功能?这也会将CarRepositoryWheelRepository移动到CarMapper中,但这是存储库的合适位置吗?

如果有人能为上面的代码展示一个很好的实用示例,我会很高兴的。

更多信息

我不使用ORM,而是使用jOOQ。它实际上只是编写SQL的一种类型安全的方法(而且它使使用SQL变得非常有趣)。

下面是一个这样的例子:

代码语言:javascript
复制
public List<CompanyDTO> getCompanies(Long adminId) {

    LOGGER.debug("Loading companies for user ..");

    Table<?> companyEmployee = this.ctx.select(COMPANY_EMPLOYEE.COMPANY_ID)
        .from(COMPANY_EMPLOYEE)
        .where(COMPANY_EMPLOYEE.ADMIN_ID.eq(adminId))
        .asTable("companyEmployee");

    List<CompanyDTO> fetchInto = this.ctx.select(COMPANY.ID, COMPANY.NAME)
        .from(COMPANY)
        .join(companyEmployee)
            .on(companyEmployee.field(COMPANY_EMPLOYEE.COMPANY_ID).eq(COMPANY.ID))
            .fetchInto(CompanyDTO.class);

    return fetchInto;
}
EN

回答 2

Stack Overflow用户

发布于 2016-09-17 17:38:44

模式库属于数据访问对象的模式组,通常意味着--对相同类型的对象的存储的抽象。想想您可以用来存储对象的Java集合--它有哪些方法?它是如何运作的?

根据这一定义,存储库不能使用DTOs -它是--一个域实体的存储。如果您只有DTO,那么您需要更通用的DAO,或者,可能是CQRS模式。有一个存储库的独立接口和实现是很常见的,比如在Spring中(它会自动生成实现,所以您只需要指定接口,很可能是从公共超级接口CrudRepository继承基本CRUD操作)。示例:

代码语言:javascript
复制
class Car {
   private long ownerId;
   private List<Wheel> wheels;
}

@Repository 
interface CarRepository extends CrudRepository<Car,Long> {
   List<Car> findByOwnerId(long id);
}

当您的域模型是一棵对象树并将它们存储在关系数据库中时,事情就变得复杂了。根据这个问题的定义,您需要一个ORM。将关系内容加载到对象模型中的每一段代码都是ORM,因此您的存储库将有一个ORM作为实现。通常,JPA会在场景后面连接对象,更简单的解决方案,比如基于JOOQ或普通JDBC的定制映射器,必须手动完成。没有任何解决所有ORM问题的灵丹妙药能够有效和正确地解决:如果您选择编写自定义映射,最好还是将布线保留在存储库中,这样业务层(服务)就可以使用真正的对象模型操作。在您的示例中,CarRepository了解Cars。Car知道Wheels,因此CarRepository已经对Wheel具有传递依赖关系。在CarRepository#findByOwnerId()方法中,您可以通过添加一个join为同一查询中的汽车直接获取Wheels,或者将此任务委托给WheelRepository,然后只进行连接。此方法的用户将收到完全初始化的对象树。示例:

代码语言:javascript
复制
class CarRepositoryImpl implements CarRepository {

  public List<Car> findByOwnerId(long id) {
     // pseudocode for some database query interface
     String sql = select(CARS).join(WHEELS); 
     final Map<Long, Car> carIndex = new HashMap<>();
     execute(sql, record -> { 
          long carId = record.get(CAR_ID);
          Car car = carIndex.putIfAbsent(carId, Car::new);
          ... // map the car if necessary
          Wheel wheel = ...; // map the wheel
          car.addWheel(wheel);
     }); 
     return carIndex.values().stream().collect(toList());
  }
}

业务层(有时也称为服务层)的作用是什么?业务层对对象执行业务特定的操作,如果这些操作必须是原子的,则管理事务。基本上,它知道何时发出事务启动、事务提交和回滚的信号,但不知道这些消息实际上将在事务管理器实现中触发什么。从业务层的角度来看,只有对对象、边界和事务隔离的操作,而没有其他操作。它不必知道映射器或任何位于存储库接口后面的东西。

票数 1
EN

Stack Overflow用户

发布于 2016-09-18 03:26:36

在我看来,没有正确的答案。这实际上取决于所选择的设计决策,它本身取决于您的堆栈和您/团队的舒适度。

案例1:

我不同意你在发言中强调的以下各节:

“当然,我可以在wheelMap中加载CarRepository,在那里执行轮询映射,并且只返回完整的对象,但是由于查询有时看起来非常复杂,所以我不想获取所有的汽车及其车轮,还要处理getCars(Long OwnerId)中的映射。”

上面的sql将很简单。此外,由于数据库是为连接和获取数据而优化的,所以它可能会更快。现在,我将这种方法称为Case1,因为如果您决定使用sql通过存储库提取数据,就可以遵循这种方法。而不是仅仅将sql用于简单的CRUD,然后在java中操作对象。(下文)

Case2:存储库仅用于获取与“一个”表对应的“每个”域对象的数据。

在这种情况下,您所做的已经是正确的。如果您永远不会单独使用WheelDTO,那么就没有必要为它提供单独的服务。你可以准备所有的汽车服务。但是,如果您单独需要WheelDTO,则为每个服务提供不同的服务。在这种情况下,在服务层的顶部可以有一个助手层来执行对象创建。我不建议从零开始实现orm,方法是为每个回购(直接使用hibernate或)创建存储库并加载所有联接。

同样,IMHO,无论您从上面的任何方法,服务或业务层只是补充它。所以,没有硬和快速的规则,试着灵活的根据你的要求。如果您决定使用ORM,上面的一些内容将再次更改。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39128750

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档