Доменная служба против службы приложений на примере - PullRequest
0 голосов
/ 15 января 2020

Я знаю, что есть много вопросов (и ответов) о разнице между службой домена и службой приложения.

Один из наиболее часто просматриваемых ответов по этому вопросу: Проект, управляемый доменом: Доменная служба, служба приложений

Но мне все еще не удается провести черту между этими двумя видами услуг. Итак, я привел здесь пример.

Это моя сущность:

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 ?

Спасибо!

1 Ответ

1 голос
/ 16 января 2020

Для меня, в любом случае, различие заключается в том, что служба приложений используется на уровне интеграции / концерне. Интеграция появляется на периферии вашего решения, где «внешние» (внешние интерфейсы) обращаются к «внутренним» (web-api / обработчикам сообщений).

Как таковые, они обычно не получают входные данные в терминах доменные объекты, а скорее в примитивах, таких как идентификаторы и необработанные данные. Если взаимодействие достаточно простое, тогда объект, выполняющий взаимодействие (контроллер / процессор сообщений), может напрямую использовать механизм хранилища или запроса. На уровне интеграции выполняется обработка транзакций (начало / принятие).

Однако, если ваше взаимодействие требует согласования между двумя или более объектами домена, вы обычно выбираете службу приложений и передаете примитивные данные в , что . Вы могли бы , опять же, выполнить взаимодействие самостоятельно, кодируя все это в объекте, выполняющем взаимодействие (контроллер / процессор сообщений). Если вы обнаружите, что вы дублируете код, то, безусловно, требуется служба приложения.

Службы приложения, таким образом, будут выполнять сбор любых дополнительных данных, передаваемых в домен.

Служба домена с другой стороны, обычно работает непосредственно с объектами домена. Это не делает никакого дополнительного сбора данных. Мне нравится передавать все, что нужно домену на домен. Домен не должен вызывать , чтобы получить что-то дополнительное. Я также отошел от двойной отправки и скорее выполняю соответствующий вызов за пределами домена. Если задействован только один объект, вы можете проверить, нельзя ли перенести функциональность на сам объект домена. Например, вы можете go с driver.HiredBy(company);, а инварианты можно применять в методе HiredBy. То же самое с Fired(). Кроме того, мне нравится возвращать доменные события от самих объектов: в классе Driver у нас может быть DriverFirstEvent Fire(Company currentCompany);

Эти рекомендации могут отличаться в зависимости от ваших требований, поскольку ничто не вылито из камня.

То, что у вас есть в качестве примера, я бы отнес к категории службы приложений .

...