0

我知道关于域服务和应用程序服务之间的区别有很多问题(和答案)。

与此相关的最受欢迎的答案之一是:领域驱动设计:领域服务、应用程序服务

但是我仍然无法在这两种服务之间划清界限。所以我在这里举了一个例子。

这是我拥有的实体:

package com.transportifygame.core.domain.entities;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.transportifygame.core.domain.constants.Drivers;
import lombok.Getter;
import lombok.Setter;

import javax.persistence.*;
import java.time.ZonedDateTime;
import java.util.UUID;

@Getter
@Setter

@Entity
@Table(name = "drivers")
public class Driver
{
    @Id
    @GeneratedValue
    private UUID id;

    @Column(name = "name", nullable = false)
    private String name;

    @Column(name = "salary", nullable = false)
    private Double salary;

    @Column(name = "age")
    private Integer age;

    @Column(name = "hired_at")
    private ZonedDateTime hiredAt;

    @Column(name = "bonus")
    private Integer bonus;

    @Column(name = "experience_level", nullable = false)
    private Integer experienceLevel = Drivers.ExperienceLevel.BEGINNER.ordinal();

// And keep going...
}

这是我拥有的域服务

package com.transportifygame.core.domain.services;

import com.transportifygame.core.domain.entities.Company;
import com.transportifygame.core.domain.entities.Driver;
import com.transportifygame.core.domain.events.drivers.DriverFired;
import com.transportifygame.core.domain.exceptions.drivers.DriverInDeliveryException;
import com.transportifygame.core.domain.exceptions.drivers.DriverNotFoundException;
import com.transportifygame.core.application.repositories.DriverRepository;
import com.transportifygame.core.application.utils.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.transaction.Transactional;
import java.util.UUID;

@Service
public class DriverService extends AbstractService
{
    private DriverRepository driverRepository;
    private DriverAvailableService driverAvailableService;

    @Autowired
    public DriverService(
        DriverRepository driverRepository,
        DriverAvailableService driverAvailableService
    )
    {
        this.driverRepository = driverRepository;
        this.driverAvailableService = driverAvailableService;
    }

    @Transactional
    public Driver hire(Company company, UUID driverAvailableId) throws DriverNotFoundException
    {
        // First load the driver
        var driver = this.driverAvailableService.getDriver(driverAvailableId);

        // copy the data from the driver available
        var newDriver = new Driver();
        newDriver.setName(driver.getName());
        newDriver.setAge(driver.getAge());
        newDriver.setBonus(driver.getBonus());
        newDriver.setHiredAt(DateTime.getCurrentDateTime(company.getUser().getTimezone()));
        newDriver.setSalary(driver.getSalary());
        newDriver.setCompany(company);

        // save it
        newDriver = this.driverRepository.save(newDriver);
        this.driverAvailableService.deleteDriver(driver);

        return newDriver;
    }

    public void fire(Company company, UUID driverId) throws DriverInDeliveryException, DriverNotFoundException
    {
        var driver = this.getDriverDetails(driverId);
        if (!driver.getCompany().getId().equals(company.getId())) {
            throw new DriverNotFoundException();
        }

        // First check if the driver it's in the middle of a delivery
        if (driver.getCurrentDelivery() != null) {
            throw new DriverInDeliveryException();
        }

        var driverFiredEvent = new DriverFired(this, company, driver.getName(), driver.getSalary());
        this.publishEvent(driverFiredEvent);

        // And delete the driver in the end
        this.driverRepository.delete(driver);
    }

    public Iterable<Driver> getAllCompanyDrivers(Company company)
    {
        return this.driverRepository.findAllByCompanyId(company.getId());
    }

    public Driver getDriverDetails(UUID id) throws DriverNotFoundException
    {
        var driver = this.driverRepository.findById(id);

        if (driver.isEmpty()) {
            throw new DriverNotFoundException();
        }

        return driver.get();
    }
}

该服务可以归类为域服务吗?如果不是,可以将其切分为ApplicationService什么?

谢谢!

4

1 回答 1

3

无论如何,对我来说,区别在于集成层/关注点中使用了应用程序服务。集成出现在解决方案的外围,其中“外部”(前端)访问“内部”(web-api / 消息处理器)。

因此,它们通常不接收域对象的输入,而是接收诸如 Id 和原始数据之类的原语。如果交互足够简单,那么执行交互的对象(控制器/消息处理器)可以直接使用存储库或查询机制。集成层是您执行事务处理(开始/提交)的地方。

但是,如果您的交互需要在两个或多个域对象之间进行编排,那么您通常会选择应用程序服务并将原始数据传递给. 再次,您可以通过对执行交互的对象(控制器/消息处理器)中的所有内容进行编码来自己执行交互。如果您发现您正在复制代码,那么肯定需要应用程序服务。

因此,应用程序服务将收集要传递给域的任何附加数据。

另一方面,域服务通常直接对域对象进行操作。它不进行任何额外的数据收集。我喜欢将域所需的所有内容传递域。该域不应该需要调用来获得任何额外的东西。我也不再使用双重调度,而是在域外执行相关调用。如果只涉及一个对象,您可能需要检查该功能是否无法移动到域对象本身。例如,您可以使用driver.HiredBy(company);并且可以在HiredBy方法中应用不变量。与 相同Fired()。另外,我喜欢从对象本身返回域事件:在Driver我们可以拥有的类上DriverFirstEvent Fire(Company currentCompany);

这些指南可能会根据您的要求而有所不同,因为没有什么是一成不变的。

您所拥有的作为示例,我会将其归类为应用程序服务

于 2020-01-16T09:04:28.820 回答