Должен ли сервисный уровень принимать DTO или пользовательский объект запроса от контроллера? - PullRequest
0 голосов
/ 29 декабря 2018

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

Ниже я выдвинул три моих собственных предложения:

Метод 1: В этом методе объект домена (Item) сохраняется на уровне службы.

class Controller
{
    @Autowired
    private ItemService service;

    public ItemDTO createItem(IntemDTO dto)
    {
        // service layer returns a DTO object and accepts a DTO object
        return service.createItem(dto);
    }
}

Метод 2: Здесь сервисный уровень получает объект пользовательского запроса.Я широко видел этот шаблон в AWS Java SDK, а также в Google Cloud Java API

class Controller
{
    @Autowired
    private ItemService service;

    public ItemDTO createItem(CreateItemRequest request)
    {
        // service layer returns a DTO object and accepts a custom request object
        return service.createItem(request);
    }
}

Метод 3: Сервисный уровень принимает DTO и возвращает объект домена.Я не фанат этого метода.Но он широко использовался на моем рабочем месте.

class Controller
{
    @Autowired
    private ItemService service;

    public ItemDTO createItem(CreateItemRequest request)
    {
        // service layer returns a DTO object and accepts a DTO object
        Item item = service.createItem(request);
        return ItemDTO.fromEntity(item);
    }
}

Если все 3 из вышеперечисленных методов неверны или не лучший способ сделать это, пожалуйста, сообщите мне о наилучшей практике.

Ответы [ 2 ]

0 голосов
/ 31 декабря 2018

Я из C# фона, но концепция здесь остается прежней.

В такой ситуации, когда мы должны передать параметры / состояние от уровня приложения к уровню обслуживания и затем вернуть результатот уровня обслуживания я бы склонялся к разделению проблем.Сервисному уровню не нужно знать о параметре Request вашего прикладного уровня / контроллера.Точно так же то, что вы возвращаете с уровня обслуживания, не должно сочетаться с тем, что вы возвращаете с вашего контроллера.Это разные слои, разные требования, отдельные проблемы.Мы должны избегать тесной связи.

Для приведенного выше примера я бы сделал что-то вроде этого:

class Controller
{
     @Autowired
     private ItemService service;

     public ItemResponse createItem(CreateItemRequest request)
     {
        var creatItemDto = GetDTo(request);
        var itemDto = service.createItem(createItemDto);
        return GetItemResponse(itemDto);
    }
}

Это может показаться дополнительной работой, так как теперь вам нужно написать дополнительный код для преобразованияразные предметы.Однако это дает вам большую гибкость и значительно упрощает поддержку кода.Например: CreateItemDto может иметь дополнительные / вычислительные поля по сравнению с CreateItemRequest.В таких случаях вам не нужно открывать эти поля в вашем Request объекте.Вы только выставляете свой Data Contract клиенту и ничего более.Точно так же вы возвращаете клиенту только соответствующие поля по сравнению с тем, что вы возвращаете из сервисного уровня.

Если вы хотите избежать ручного отображения между Dto и Request objects C #, есть библиотеки вроде AutoMapper.Я уверен, что в мире Java должен быть эквивалент.Может быть ModelMapper может помочь.

0 голосов
/ 30 декабря 2018

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

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

Требуется решение, в котором контракты методов уровня приложения (параметры и тип возврата) выражаются в любых собственных типах или типах Java, определенных на границе уровня обслуживания.

Если мы примем образец IDDD от Вона Вернона, мы можем видеть, что его контракты с методами обслуживания приложений определены в собственных типах Java.Его методы команд службы приложений также не дают никакого результата, если он использовал CQRS, но мы видим, что методы запроса действительно возвращают DTO, определенный в пакете уровня приложения / службы.

В перечисленных выше 3 методах, какие из них правильные / неправильные?

Оба, # 1 и # 2, очень похожи и могут быть правильными с точки зрения зависимости, пока ItemDto и CreateItemRequest определены в пакете прикладного уровня, но я бы предпочел № 2, поскольку тип входных данных именуется в зависимости от варианта использования, а не просто от типа сущности, с которой он имеет дело: сущность именования-фокус лучше подходит для CRUD и из-зачто вам может быть трудно найти хорошие имена для входных типов данных других методов прецедентов, работающих на объектах того же типа.# 2 также был популяризирован через CQRS (где команды обычно отправляются на командную шину), но не является эксклюзивным для CQRS.Вон Вернон также использует этот подход в образцах IDDD .Обратите внимание, что то, что вы называете request , обычно называется command .

Однако # 3 не будет идеальным, учитывая, что он связывает контроллер (уровень представления) с доменом.

Например, некоторые методы получают 4 или 5 аргументов.По мнению Эрика Эванса из «Чистого кода», таких методов следует избегать.

Это хорошее руководство, которому нужно следовать, и я не говорю, что образцы не могут быть улучшены, но имейте в виду, что в DDDосновное внимание уделяется наименованию вещей в соответствии с Ubiquitous Language (UL) и следованию за ним как можно ближе.Следовательно, внедрение новых концепций в проект только для группировки аргументов может быть вредным.По иронии судьбы, процесс попыток сделать это может все же дать некоторые хорошие идеи и позволить обнаружить пропущенные и полезные концепции предметной области, которые могут обогатить UL.

PS: Роберт К. Мартин написал «Чистый код»не Эрик Эванс, который славится синей книгой.

...