Говоря о шлюзах из Чистой архитектуры, необходимо различать:
Пример на TypeScript:
// === Interface ======================================
interface UsersGateway {
retrieveSelection: (requiestParameters: UsersGateway.SelectionRetireving.RequestParameters) =>
Promise<UsersGateway.SelectionRetireving.ResponseData>;
}
namespace UsersGateway {
export namespace SelectionRetireving {
export type RequestParameters = Readonly<{
paginationPageNumber: number;
itemsCountPerPaginationPage: number;
fullOrPartialName? string;
}>;
export type ResponseData = Readonly<{
totalItemsCount: number;
filteredItemsCount: number;
itemsForCurrentPaginationPage: ReadonlyArray<User>;
}>;
}
}
// === Implementation =======================================
class UserMySQL_Gateway implements UserGateway {
retrieveSelection(
requiestParameters: UsersGateway.SelectionRetireving.RequestParameters
): Promise<UsersGateway.SelectionRetireving.ResponseData> {
// Implementation including SQL code here
}
}
Теперь вопрос: на приведенном ниже изображении из книги Clean Architecture, "шлюзы" в зеленом кольце являются интерфейсами их реализаций? Если один из них, то есть и другой?
Хотя фреймворки должны быть во внешнем (синем) кольце и, я не видел ранее независимых от фреймворка контроллеров и/или презентеров. Таким образом, "шлюзы" в зеленом кольце могут быть реализациями, которые зависят от какого-то фреймворка или СУБД (что соответствует концепции адаптера интерфейса).
Если да, то есть ли интерфейсы шлюза? Оглавление главы 20 Правила ведения бизнеса:
Являются ли перечисленные выше модели запроса и ответа интерфейсами шлюза?
Не видя больше кода, чем то, что вы предоставили в своем примере Typescript, невозможно быть уверенным на 100%, но я бы поспорил, что следующее примерно правильно (если нет, просто мысленно сдвиньте его на один слой).
Модели запроса и ответа — это только ваши RequestParameters
и ResponseData
. Ваш интерфейс UsersGateway
, RequestParameters
и ResponseData
принадлежат красному слою вариантов использования (они «принадлежат» или служат потребностям какого-то Interactor — что предположительно имеет какое-то отношение к пользователям). Интерфейс UsersGateway
подобен выходному порту (изображен на вставке) в том смысле, что он вызывается Interactor, за исключением того, что вместо этого он используется для извлечения данных (Interactor решает, когда инициировать извлечение; напротив, входной порт является вызывается вещами в следующем слое). Конструктор Interactor будет полиморфно принимать конкретный шлюз в качестве параметра UsersGateway
.
Типы RequestParameters
и ResponseData
связаны с UsersGateway
, поэтому в некотором смысле они вместе определяют более широкий интерфейс (или API) для шлюза (то есть класс, реализующий UsersGateway
, также должен использовать эти типы запросов и ответов). Вот почему все они перечислены в разделе «Интерфейс» во фрагменте кода.
Реализация UserMySQL_Gateway
находится на зеленом уровне интерфейсных адаптеров — она «адаптирует» интерфейс UsersGateway
, требуемый Interactor, и подключает его к интерфейсу (API), предоставляемому любой библиотекой доступа к данным, которую вы используете. Вы должны ввести экземпляр UserMySQL_Gateway
в качестве вышеупомянутого параметра UsersGateway
. Тот факт, что интерфейс UsersGateway
сам по себе определен внутри внутреннего слоя (и для конкретных нужд Interactor), приводит к инверсии зависимостей.
Теперь Чистая Архитектура не требует точного количества слоев — вы можете использовать больше или меньше, чем показано, в зависимости от ваших потребностей. В вашем примере с TypeScript UserMySQL_Gateway
напрямую зависит от фреймворка/библиотеки; это нормально, потому что сам Interactor отвязан через интерфейс UsersGateway
. Таким образом, в некотором смысле правило зависимости нарушается на самом внешнем уровне, но это всегда будет происходить на самом внешнем уровне - в какой-то момент вам придется вызывать свои фреймворки/библиотеки. В качестве альтернативы вы можете думать о том, что синий слой Framework & Drivers объединяется с зеленым, сохраняя правило зависимости глобально. В принципе, вы можете решить отделить UserMySQL_Gateway
, применяя инверсию зависимостей аналогичным образом, если логика особенно сложна, и вы хотите ввести принадлежащий UserMySQL_Gateway
интерфейс, который (1) поможет упростить код и сделать его более читабельным, и (2) позволить вам провести модульное тестирование шлюза.
См. также мой ответ на этот вопрос — в нем обсуждается, как кто-то, кто более знаком с написанием процедурного и/или CRUD-кода, может прийти к дизайну, похожему на чистую архитектуру.