В архитектуре onion, hexagonal или clean может ли модель предметной области содержать свойства, отличные от свойств модели предметной области в базе данных?

#software-design #n-tier-architecture #clean-architecture #onion-architecture #hexagonal-architecture

#разработка программного обеспечения #n-tier-architecture #чистая архитектура #onion-архитектура #шестиугольная архитектура #программное обеспечение-дизайн #n-уровневая архитектура #лук-архитектура

Вопрос:

Я спрашиваю вас, кто хорошо знает и имеет опыт создания программного обеспечения с использованием любой многоуровневой архитектуры (onion, hexagonal, clean и т.д.). Всякий раз, когда я гуглю об архитектуре программного обеспечения, люди имеют разные точки зрения и объясняют одну и ту же архитектуру по-разному.

Условия

Прежде чем вы прочтете вопрос, некоторые термины могут вас смутить, поэтому я определяю их ниже. Я не уверен, есть ли у меня «правильные» определения для них, но я собрал эту информацию из Интернета. Дайте мне знать, если я неправильно понимаю.

Уровень домена: содержит корпоративную / бизнес-логику и использует модель домена. расположена в центре и не зависит ни от каких других уровней, кроме модели предметной области.

Прикладной уровень: содержит логику приложения, принимает DTO с уровня инфраструктуры и передает модель представления

DTO (объект передачи данных): класс, строка JSON и т.д., Используемые для передачи данных между входящими и исходящими слоями. может быть чистым контейнером данных.

Виртуальная машина (модель представления): DTO, который передается на уровень представления с уровня приложения.

DO (модель домена): класс, строка JSON и т.д., Используемые на уровне домена. может быть чистым контейнером данных.

VO (объект значения): объект базы данных (строка базы данных) или формат данных, используемый базой данных. может быть перенесен на уровень приложения из уровня базы данных.

Краткие сведения

В архитектуре onion, hexagonal или clean уровень домена находится в центре (т. Е. уровень домена не зависит ни от каких уровней, кроме модели домена, которая используется для передачи данных на другой уровень или приема данных с более высокого уровня).

Это означает, что модель домена (DTO, POJO, VO или любая другая), используемая доменом, может отличаться от модели, используемой базами данных для сохранения постоянных данных.

Я нарисовал диаграмму, чтобы дать вам лучшее объяснение.

введите описание изображения здесь

введите описание изображения здесь

Вопрос 1:

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

Если уровень домена находится в центре, в отличие от традиционной многоуровневой или n-уровневой архитектуры, может ли модель домена иметь больше свойств (или отличающихся свойств), чем объект базы данных (строка)?

Например, предположим, что уровень домена использует класс с именем Person. Пользователь запрашивает фотографии всех людей, зарегистрированных на сервере. Предположим, что база данных содержит только имена всех пользователей. Однако мы можем использовать другой веб-сервер для запроса изображения пользователя по имени. Таким образом, прикладной уровень будет считывать все имена из базы данных, и с этими именами он получит все изображения с другого веб-сервера через HTTP-запрос. После этого список пользователя с именем и изображением будет отправлен пользователю в качестве модели представления (DTO).

Вопрос 2:

Уровень сохраняемости может состоять из базы данных, файловой системы, другого веб-API и т.д.

Уровень представления может быть веб-сайтом, настольным приложением, мобильным приложением, веб-API и т.д.

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

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

В большинстве случаев прикладному уровню необходимо получать данные с уровня сохраняемости.

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

Вот как я понимаю, может ли кто-нибудь дать мне четкое объяснение, как должны передаваться данные и как осуществляется связь с нижнего уровня на более высокий уровень?

Для тех, кто хочет писать код, я предпочитаю C #.

Ответ №1:

Q1: > может ли модель домена иметь больше свойств (или отличающихся свойств), чем объект базы данных (строка)?

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

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

Вот как я понимаю до сих пор, может ли кто-нибудь дать мне четкое объяснение, как должны передаваться данные и как осуществляется передача данных с нижнего уровня на более высокий уровень?

Есть один способ. Это называется инверсией зависимостей. Если вы занимаетесь структурированным программированием, ваш код будет выглядеть следующим образом:

  -----    f()     ----- 
|  A  |  ----->  |  B  |
 -----            ----- 
  

Существует класс, A который вызывает метод f для class B .

Если вы используете C #, вы увидите using B в классе A . Если вы используете Java, это будет import B . Независимо от того, какой язык программирования вы используете. Название класса B появится в A .

Но если это оператор using или import , это означает, что компилятор знает. Таким образом, у вас есть зависимость от времени компиляции A -> B .

Когда код выполняется, поток управления (зависимость от времени выполнения) также является A -> B .

Давайте рассмотрим другой подход

  -----    f()     ------------         ------- 
|  A  |  ----->  | BInterface | <---- | BImpl |
 -----            ------------         ------- 
  

В этом сценарии A зависит от абстракции первого, B которую я вызываю здесь BInterface , и реализация перемещается в класс, BImpl реализующий этот интерфейс.

Во время выполнения поток управления по-прежнему переходит из A в метод f BImpl , но во время компиляции A и BImpl зависит от BInterface , и, следовательно, зависимость от BImpl to BInterface указывает на поток управления.

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

Вернемся к вашему вопросу

Ваш прикладной уровень должен определять интерфейс, который он может использовать для сбора объектов. Этот интерфейс часто называют Repository . Затем ваш уровень db может реализовать это Repository (инверсия зависимостей).

В чистой архитектуре это будет выглядеть следующим образом

чистая архитектура

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

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

Интерфейс является абстракцией и, таким образом, сообщает, что может быть сделано, а не как это делается.

 public interface PersonRepository {

    // WRONG - because the where is usually a part of an SQL or JPQL
    // and thus exposes the implementation.
    public List<Person> findByCriteria(String where);
} 
  

лучшим подходом было бы

 public interface PersonRepository {

    public List<Person> findByCriteria(PersonCriteria criteria);
} 

public class PersonCriteria {
    
      private String firstName;
      private MatchMode firstNameMatchMode; // something like STARTS_WITH, ENDS_WITH, CONTAINS

      // setters omitted
}
  

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

Ответ №2:

Вопрос 1: Может ли модель предметной области иметь больше свойств (или отличающихся свойств), чем объект базы данных (строка)?

Конечно. Обе модели могут иметь разные свойства. Реализация «порта сохранения» («репозитория»), то есть адаптер, будет преобразовывать одну модель друг в друга.

Вопрос 2:

В большинстве случаев прикладному уровню необходимо получать данные с уровня сохраняемости.

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

Для получения данных с уровня сохранения прикладной уровень вызывает «хранилище» (DDD lingo), то есть «порт для сохранения данных» (hex arch lingo). Это хранилище (порт) принадлежит домену, поэтому уровень приложения вызывает уровень домена.

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

Если вам интересно, вот мои статьи о шестиугольной архитектуре:

https://jmgarridopaz.github.io/content/articles.html