域服务与应用程序服务示例

问题描述 投票:0回答:2

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

有关此问题浏览次数最多的答案之一是:领域驱动设计:领域服务、应用程序服务

但是我仍然很难区分这两种服务。所以我在这里举了一个例子。

这是我拥有的实体:

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

谢谢!

java domain-driven-design
2个回答
4
投票

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

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

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

另一方面,领域服务通常直接在领域对象上运行。它不进行任何额外的数据收集。我喜欢将域需要的所有内容传递给域。该域不需要

调用 来获取任何额外的东西。我也放弃了doubledispatch,而是在域外执行相关调用。如果只涉及一个对象,您可能需要检查功能是否无法移动到域对象本身。例如,您可以使用 driver.HiredBy(company);,并且可以在 HiredBy 方法中应用不变量。与

Fired()
相同。另外,我喜欢从对象本身返回域事件:在
Driver
类上,我们可以有
DriverFirstEvent Fire(Company currentCompany);
这些准则可能会根据您的要求而有所不同,因为没有什么是一成不变的。
您所拥有的样本,我将其归类为 

应用程序服务

这部分:


0
投票

可以移动到域对象:

    if (!driver.isWorkingInGivenCompany(company)) {
        throw new DriverNotFoundException();
    }

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

这也可以移动到domainObject:

// First load the driver
    AvalibleDriver avalibleDriver = this.driverAvailableService.getDriver(driverAvailableId);

    // copy the data from the driver available
    var newDriver = Driver.of(avalibleDriver);

然后您可以在“of”方法中添加验证,例如名称不能为空等。

这个想法是在 doimanObject 中保留仅涉及域对象并且不需要外部信息的逻辑。因此它将是可重用的并且很容易在代码中找到。

© www.soinside.com 2019 - 2024. All rights reserved.