Гайд Разработка сервиса отзывов. Часть 0.0.1. Архитектура

Модератор форума
Участник
Участник
Статус
Оффлайн
Регистрация
26 Янв 2020
Сообщения
378
Реакции
157
Всем привет, на связи член модераторского состава форума yougame.biz, по совместительству бэкенд разработчик в аутсорсиноговой компании. На пути своего становления в этом ремесле, столкнулся с проблемой, что информация в интернете по бэкэнд разработке, в особенности на spring boot, в некоторых моментах страдает плохим качеством, а то и вовсе отсутствует. Поэтому сформировать полноценный пласт знаний в этой области начинающему разработчику бывает достаточно сложно. В связи с этим, появилась мотивация сделать цикл статей, в которых с самого нуля будет рассмотрено создание демо - сервиса, в котором будут отражены все основные аспекты, которыми должен владеть начинающий разработчик.

В этой статье пойдет речь о фундаменте любого приложения, не важно, web это или десктоп. Без понимания этой концепции строить надежные и легко расширяемые приложения будет практически невозможно. И говорить мы будем об Архитектуре.

Как скомпоновать приложение? Какие в нём должны быть слои? Как назвать пакеты? Где расположить DTO, маперы, реализации интерфейсов? И нужны ли вообще интерфейсы?

Когда человек начинает делать свой первый проект, то у него нету однозначных ответов на эти вопросы. Он смотрит код, "до которого может дотянуться" - открытые GitHub репозитории, обучающие статьи, и если повезет, то у новичка есть все шансы научиться писать хороший, чистый, код. Если же не повезёт, то человек будет цепляться за то, что есть, нахватается плохих практик, и по прошествии года-двух он уже сам будет себе авторитетом, которого не так-то просто будет переубедить.

В посте ниже я описываю личный и командный опыт, под который я постарался подвести теоретическую базу, опираясь на "Чистую архитектуру" Роберта Мартина. Да, этот пост - для новичков, хотя, эта тема жива и среди устоявшихся программистов, поскольку споры о компоновке приложения не утихают и среди сениоров.

Зайду я немного издалека и напомню, что такое луковичная архитектура.

Что такое луковичная архитектура?

В "Чистой архитектуре" Роберта Мартина описывается центрическая архитектура, ядром которой является бизнес-сущность.

Луковичная архитектура


Давайте же попробуем разобрать, что происходит на изображении, пройдясь по каждому из слоев.

Бизнес-сущность (Entity)

Бизнес-сущность (по англ. Entity) ничего не знает ни о логике приложения, ни о слое доступа к данным, ни уж тем более о подключаемых внешних интерфейсах - она содержит только собственные поля и базовые методы для работы с ними - геттеры, сеттеры, контроллеры, билдеры и всё, что необходимо сущности для работы с самой собой. Как правило, Entity необходим для отображения данных, с которыми работает java приложение в базу данных, именно поэтому entity чаще всего аннотируется, в соответствии с используемой в проекте спецификацией для работы с бд (JDBC, JPA) Вот тут, конечно, стоило бы оговориться, что при использовании Entity как объекта для отображения данных из бд в код и наоборот, нарушается принцип независимости, но в производственной сфере мало кто обращает на это внимание, так что опустим этот момент ;)
Пример:
1719081008288.png


Репозиторий (Repository)

Данный слой стоит на один уровень выше Entity. Основная его цель - обеспечить основные операции над данными (так называемый CRUD) между бд и приложением. На вход Repository получает сформированный Entity, на выходе он должен предоставить тоже Entity, то с до заполненным полями из бд, если таковые имеются (database-generative fields - поля, которые генерирует база данных), ничего более репозиторий уметь не должен.

Пример:

1719081449865.png


На самом деле, репозиторий не обязательно должен выглядеть именно так, его внешний вид напрямую зависит от, опять же, используемой спецификации общения бд и приложения.

Сервисы (Services)

Вот это уже поинтереснее. По факту, сервисы являются сердцем любого web приложения, именно они реализуют бизнес логику. Сервис общается только с одним репозиторием, это важно! Если в приложении вы видите такую цепочку зависимостей: Entity->Repository->Service, то это означает, что приложение проектировали осознанные люди :) Старайтесь всегда соблюдать это правило - на один энтити один репозиторий, на один репозиторий один сервис. Если вам нужно обращаться к другим энтити, сделайте межсервисное взаимодействие.

Как раз о нем. Формат общения сервисов друг с другом и с вышестоящими слоями заключается в использовании так называемых DTO - data transfer objects. Это обычные классы, которые так же содержат только собственные поля и базовые методы для работы с ними - геттеры, сеттеры, контроллеры, билдеры и всё, что необходимо сущности для работы с самой собой. DTO бывает двух видов - RequestDTO и ResponceDTO.

RequestDTO содержит только те поля, которые необходимы для выполнения вызова сервисом, ничего более.

ResponceDTO, как правило, содержит в себе отображение Entity, хотя вполне допускается и дополнительная информация.


Контроллер (Controller)

Является слоем, отвечающим за отображение данных service в формат, который понимает клиент. Контроллеры бывают разных типов, как пример RestController - взаимодействует с пользователем при помощи RestAPI.

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


В итоге мы получил следующую иерархию проекта:

Код:
Expand Collapse Copy
/src
    /controller
    /service
    /repository
    /model
        /entity
        /DTO
            /request
            /responce


Буду рад услышать комментарии и дополнения к этой статье. До встречи!
 
Последнее редактирование:
имба автору +реп научился делать читы
 
Я бы еще сделал пакет для эксепшенов бизнес логики

Код:
Expand Collapse Copy
/src
    /controller
    /service
    /repository
    /model
        /entity
        /DTO
            /request
            /responce
    /exception

И сделал бы что-то такое, кидал бы соответствующие исключение, при бизнес ошибке(что-то ответило не так и тп)
Java:
Expand Collapse Copy
public class BusinessException extends RuntimeException implements Serializable {
    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

    public BusinessException(Throwable cause) {
        super(cause);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }
}

public class SomeBusinessException extends BusinessException {

    public SomeException(String message) {
        super(message);
    }

    public SomeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
 
Я бы еще сделал пакет для эксепшенов бизнес логики

Код:
Expand Collapse Copy
/src
    /controller
    /service
    /repository
    /model
        /entity
        /DTO
            /request
            /responce
    /exception

И сделал бы что-то такое, кидал бы соответствующие исключение, при бизнес ошибке(что-то ответило не так и тп)
Java:
Expand Collapse Copy
public class BusinessException extends RuntimeException implements Serializable {
    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

    public BusinessException(Throwable cause) {
        super(cause);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }
}

public class SomeBusinessException extends BusinessException {

    public SomeException(String message) {
        super(message);
    }

    public SomeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

Я бы тебе дырку в башке сделал
 
Я бы еще сделал пакет для эксепшенов бизнес логики

Код:
Expand Collapse Copy
/src
    /controller
    /service
    /repository
    /model
        /entity
        /DTO
            /request
            /responce
    /exception

И сделал бы что-то такое, кидал бы соответствующие исключение, при бизнес ошибке(что-то ответило не так и тп)
Java:
Expand Collapse Copy
public class BusinessException extends RuntimeException implements Serializable {
    public BusinessException(String message) {
        super(message);
    }

    public BusinessException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }

    public BusinessException(Throwable cause) {
        super(cause);
    }

    public BusinessException(String message, Throwable cause) {
        super(message, cause);
    }
}

public class SomeBusinessException extends BusinessException {

    public SomeException(String message) {
        super(message);
    }

    public SomeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}
Спасибо, получилось!
 
Назад
Сверху Снизу