День восемьсот восемьдесят четвёртый. #DesignPatterns #Microservices
Паттерны в Микросервисах
1. База Данных на Микросервис
Когда компания заменяет большую монолитную систему множеством мелких микросервисов, самое важное решение, с которым она сталкивается, - это база данных. В монолитной архитектуре используется большая централизованная база данных. Многие архитекторы предпочитают сохранять базу данных как есть, даже при переходе на микросервисную архитектуру. Хотя это даёт некоторую краткосрочную выгоду, это анти-шаблон, особенно в крупной системе, поскольку микросервисы будут тесно связаны на уровне базы данных. Весь смысл перехода на микросервисы (расширение возможностей команды, независимая разработка) потеряется.
Лучший подход - предоставить каждому микросервису собственное хранилище данных, чтобы не было сильной связи между службами на уровне базы данных (см. рисунок ниже). Здесь термин «база данных» используется, чтобы показать логическое разделение данных, то есть микросервисы могут совместно использовать одну и ту же физическую базу данных, но они должны использовать отдельную схему/коллекцию/таблицу. Это также гарантирует, что микросервисы правильно разделены в соответствии с предметно-ориентированным проектированием (DDD).
Плюсы
- Полное владение данными для каждого сервиса.
- Слабая связь между командами, разрабатывающими сервисы.
Минусы
- Обмен данными между службами становится проблематичным.
- Предоставление транзакционной гарантии ACID для приложения становится намного сложнее.
- Разделение монолитной базы данных на более мелкие части требует тщательного проектирования и является сложной задачей.
Когда использовать:
- В крупномасштабных корпоративных приложениях.
- Когда команде необходимо полное владение своими микросервисами для масштабирования и скорости разработки.
Когда не использовать:
- В небольших приложениях.
- Если одна команда разработает все микросервисы.
Поддержка
Все базы данных SQL и NoSQL предлагают логическое разделение данных (например, отдельные таблицы, коллекции, схемы, базы данных).
Подробнее:
- Microservices Pattern: Database per service
- Распределённые данные
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
Паттерны в Микросервисах
1. База Данных на Микросервис
Когда компания заменяет большую монолитную систему множеством мелких микросервисов, самое важное решение, с которым она сталкивается, - это база данных. В монолитной архитектуре используется большая централизованная база данных. Многие архитекторы предпочитают сохранять базу данных как есть, даже при переходе на микросервисную архитектуру. Хотя это даёт некоторую краткосрочную выгоду, это анти-шаблон, особенно в крупной системе, поскольку микросервисы будут тесно связаны на уровне базы данных. Весь смысл перехода на микросервисы (расширение возможностей команды, независимая разработка) потеряется.
Лучший подход - предоставить каждому микросервису собственное хранилище данных, чтобы не было сильной связи между службами на уровне базы данных (см. рисунок ниже). Здесь термин «база данных» используется, чтобы показать логическое разделение данных, то есть микросервисы могут совместно использовать одну и ту же физическую базу данных, но они должны использовать отдельную схему/коллекцию/таблицу. Это также гарантирует, что микросервисы правильно разделены в соответствии с предметно-ориентированным проектированием (DDD).
Плюсы
- Полное владение данными для каждого сервиса.
- Слабая связь между командами, разрабатывающими сервисы.
Минусы
- Обмен данными между службами становится проблематичным.
- Предоставление транзакционной гарантии ACID для приложения становится намного сложнее.
- Разделение монолитной базы данных на более мелкие части требует тщательного проектирования и является сложной задачей.
Когда использовать:
- В крупномасштабных корпоративных приложениях.
- Когда команде необходимо полное владение своими микросервисами для масштабирования и скорости разработки.
Когда не использовать:
- В небольших приложениях.
- Если одна команда разработает все микросервисы.
Поддержка
Все базы данных SQL и NoSQL предлагают логическое разделение данных (например, отдельные таблицы, коллекции, схемы, базы данных).
Подробнее:
- Microservices Pattern: Database per service
- Распределённые данные
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
День восемьсот восемьдесят седьмой. #DesignPatterns #Microservices
Паттерны в Микросервисах
2. Источники Событий (Event Sourcing)
В микросервисной архитектуре, особенно с использованием паттерна «База данных на микросервис», микросервисы должны обмениваться данными. Для отказоустойчивых, высокомасштабируемых и отказоустойчивых систем они должны обмениваться данными асинхронно, обмениваясь Событиями. В этом случае вам может понадобиться выполнить атомарные операции, например, обновить базу данных и отправить сообщение. Если у вас есть базы данных SQL и вы хотите иметь распределённые транзакции для большого объема данных, вы не можете использовать двухфазную блокировку (2PL), поскольку она не масштабируется. Если вы используете базы данных NoSQL и хотите иметь распределённую транзакцию, вы не можете использовать 2PL, поскольку многие базы данных NoSQL не поддерживают двухфазную блокировку.
В таких сценариях используйте архитектуру на основе событий. В традиционных базах данных бизнес-объект с текущим «состоянием» сохраняется напрямую. В Event Sourcing любое событие, изменяющее состояние, или другие важные события сохраняются вместо сущностей. Это означает, что изменения бизнес-объекта сохраняются в виде серии неизменяемых событий. Состояние бизнес-объекта высчитывается путём повторной обработки всех событий этого бизнес-объекта в заданное время. Поскольку данные сохраняются в виде серии событий, а не через прямые обновления в хранилищах данных, различные службы могут воспроизводить события из хранилища событий, чтобы вычислить необходимое состояние соответствующих хранилищ данных. См. картинку ниже.
Плюсы
- Обеспечение атомарности высокомасштабируемым системам.
- Автоматическое сохранение истории сущностей, включая функционал «путешествий во времени».
- Возможность создания слабосвязанных микросервисов, управляемых событиями.
Минусы
- Чтение сущностей из хранилища событий становится сложной задачей и обычно требует дополнительного хранилища данных (паттерн CQRS)
- Общая сложность системы возрастает, и обычно требуется доменно-ориентированный дизайн.
- Система должна обрабатывать повторяющиеся события (идемпотентные) или отсутствующие события.
- Миграция схемы событий становится сложной задачей.
Когда использовать:
- Высокомасштабируемые транзакционные системы с базами данных SQL.
- Транзакционные системы с базами данных NoSQL.
- Высоко масштабируемая и устойчивая микросервисная архитектура.
- Типичные системы, управляемые сообщениями или событиями (системы электронной коммерции, бронирования и резервирования).
Когда не использовать:
- Слабо масштабируемые транзакционные системы с базами данных SQL.
- В простой микросервисной архитектуре, где микросервисы могут синхронно обмениваться данными (например, через API).
Поддержка
Хранилища: EventStoreDB, Apache Kafka, Confluent Cloud, AWS Kinesis, Azure Event Hub, GCP Pub/Sub, Azure Cosmos DB, MongoDB, Cassandra, Amazon DynamoDB.
Фреймворки: Lagom, Akka, Spring, akkatecture, Axon, Eventuate
Подробнее:
- Microservices Pattern: Event sourcing
- Шаблон источников событий
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
Паттерны в Микросервисах
2. Источники Событий (Event Sourcing)
В микросервисной архитектуре, особенно с использованием паттерна «База данных на микросервис», микросервисы должны обмениваться данными. Для отказоустойчивых, высокомасштабируемых и отказоустойчивых систем они должны обмениваться данными асинхронно, обмениваясь Событиями. В этом случае вам может понадобиться выполнить атомарные операции, например, обновить базу данных и отправить сообщение. Если у вас есть базы данных SQL и вы хотите иметь распределённые транзакции для большого объема данных, вы не можете использовать двухфазную блокировку (2PL), поскольку она не масштабируется. Если вы используете базы данных NoSQL и хотите иметь распределённую транзакцию, вы не можете использовать 2PL, поскольку многие базы данных NoSQL не поддерживают двухфазную блокировку.
В таких сценариях используйте архитектуру на основе событий. В традиционных базах данных бизнес-объект с текущим «состоянием» сохраняется напрямую. В Event Sourcing любое событие, изменяющее состояние, или другие важные события сохраняются вместо сущностей. Это означает, что изменения бизнес-объекта сохраняются в виде серии неизменяемых событий. Состояние бизнес-объекта высчитывается путём повторной обработки всех событий этого бизнес-объекта в заданное время. Поскольку данные сохраняются в виде серии событий, а не через прямые обновления в хранилищах данных, различные службы могут воспроизводить события из хранилища событий, чтобы вычислить необходимое состояние соответствующих хранилищ данных. См. картинку ниже.
Плюсы
- Обеспечение атомарности высокомасштабируемым системам.
- Автоматическое сохранение истории сущностей, включая функционал «путешествий во времени».
- Возможность создания слабосвязанных микросервисов, управляемых событиями.
Минусы
- Чтение сущностей из хранилища событий становится сложной задачей и обычно требует дополнительного хранилища данных (паттерн CQRS)
- Общая сложность системы возрастает, и обычно требуется доменно-ориентированный дизайн.
- Система должна обрабатывать повторяющиеся события (идемпотентные) или отсутствующие события.
- Миграция схемы событий становится сложной задачей.
Когда использовать:
- Высокомасштабируемые транзакционные системы с базами данных SQL.
- Транзакционные системы с базами данных NoSQL.
- Высоко масштабируемая и устойчивая микросервисная архитектура.
- Типичные системы, управляемые сообщениями или событиями (системы электронной коммерции, бронирования и резервирования).
Когда не использовать:
- Слабо масштабируемые транзакционные системы с базами данных SQL.
- В простой микросервисной архитектуре, где микросервисы могут синхронно обмениваться данными (например, через API).
Поддержка
Хранилища: EventStoreDB, Apache Kafka, Confluent Cloud, AWS Kinesis, Azure Event Hub, GCP Pub/Sub, Azure Cosmos DB, MongoDB, Cassandra, Amazon DynamoDB.
Фреймворки: Lagom, Akka, Spring, akkatecture, Axon, Eventuate
Подробнее:
- Microservices Pattern: Event sourcing
- Шаблон источников событий
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍4
День восемьсот девяносто первый. #DesignPatterns #Microservices
Паттерны в Микросервисах
3. Разделение Ответственности Запросов и Команд (CQRS)
При использовании паттерна Источников Событий иногда чтение данных из хранилища событий становится затруднительным. Чтобы получить сущность из хранилища, нам нужно обработать все события сущности. Кроме того, иногда у нас разные требования к согласованности и пропускной способности для операций чтения и записи.
В таких случаях мы можем использовать шаблон CQRS. В шаблоне CQRS функционал изменения данных системы (Команды) отделён от функционала чтения данных (Запросов). Шаблон CQRS имеет две формы: простую и расширенную, что приводит к некоторой путанице среди разработчиков.
В своей простой форме для чтения и записи используется единое хранилище данных, но отдельные сущности или модели ORM, как показано в левой части рисунка ниже. Это помогает обеспечить соблюдение принципов единственной ответственности и разделения ответственности, что приводит к более чистому дизайну.
В расширенной форме (см. в правой части рисунка ниже) для операций чтения и записи используются разные хранилища данных. Расширенный CQRS используется с паттерном Источников Событий. В зависимости от реализации могут использоваться разные типы хранилищ для записи и для чтения. Хранилище для записи - это краеугольный камень всей системы.
Для приложений с интенсивным чтением или микросервисной архитектуры в качестве хранилища для записи используется база данных OLTP (любая база данных SQL или NoSQL, предлагающая гарантию транзакции ACID) или платформа распределённого обмена сообщениями. В приложениях с интенсивной записью (для высокой масштабируемости и пропускной способности записи) используется база данных с возможностью горизонтального масштабирования для записи (глобальные облачные базы данных). В хранилище для записи сохраняются нормализованные данные.
В качестве хранилища для чтения используется база данных NoSQL, оптимизированная для поиска (например, Apache Solr, Elasticsearch) или чтения (хранилище пар ключ-значение, хранилище документов). Во многих случаях, когда требуется SQL-запрос, используются масштабируемые для чтения базы данных SQL. В хранилище для чтения сохраняются денормализованные и оптимизированные данные.
Данные копируются из хранилища для записи в хранилище для чтения асинхронно. В результате хранилище чтения отстает от хранилища записи и имеет место Окончательная Согласованность (Eventual Consistency).
Плюсы
- Более быстрое чтение данных в микросервисах, управляемых событиями.
- Высокая доступность данных.
- Системы чтения и записи могут масштабироваться независимо.
Минусы
- Хранилище для чтения слабо согласовано (окончательная согласованность)
- Общая сложность системы увеличивается. Чрезмерная увлечённость CQRS может поставить под угрозу весь проект.
Когда использовать
- В высокомасштабируемой микросервисной архитектуре, где используется паттерн источников событий.
- В сложной модели предметной области, где для чтения данных требуется запрос в несколько хранилищ данных.
- В системах, где операции чтения и записи имеют разную нагрузку.
Когда не использовать
- В микросервисной архитектуре, где объём событий незначителен, создание моментального снимка хранилища событий для вычисления состояния объекта является лучшим выбором.
- В системах, где операции чтения и записи имеют одинаковую нагрузку.
Поддержка
Хранилища для Записи: EventStoreDB, Apache Kafka, Confluent Cloud, AWS Kinesis, Azure Event Hub, GCP Pub/Sub, Azure Cosmos DB, MongoDB, Cassandra, Amazon DynamoDB
Хранилища для Чтения: Elastic Search, Solr, Cloud Spanner, Amazon Aurora, Azure Cosmos DB, Neo4j
Фреймворки: Lagom, Akka, Spring, akkatecture, Axon, Eventuate
Подробнее:
- CQRS
- Что такое шаблон CQRS?
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
Паттерны в Микросервисах
3. Разделение Ответственности Запросов и Команд (CQRS)
При использовании паттерна Источников Событий иногда чтение данных из хранилища событий становится затруднительным. Чтобы получить сущность из хранилища, нам нужно обработать все события сущности. Кроме того, иногда у нас разные требования к согласованности и пропускной способности для операций чтения и записи.
В таких случаях мы можем использовать шаблон CQRS. В шаблоне CQRS функционал изменения данных системы (Команды) отделён от функционала чтения данных (Запросов). Шаблон CQRS имеет две формы: простую и расширенную, что приводит к некоторой путанице среди разработчиков.
В своей простой форме для чтения и записи используется единое хранилище данных, но отдельные сущности или модели ORM, как показано в левой части рисунка ниже. Это помогает обеспечить соблюдение принципов единственной ответственности и разделения ответственности, что приводит к более чистому дизайну.
В расширенной форме (см. в правой части рисунка ниже) для операций чтения и записи используются разные хранилища данных. Расширенный CQRS используется с паттерном Источников Событий. В зависимости от реализации могут использоваться разные типы хранилищ для записи и для чтения. Хранилище для записи - это краеугольный камень всей системы.
Для приложений с интенсивным чтением или микросервисной архитектуры в качестве хранилища для записи используется база данных OLTP (любая база данных SQL или NoSQL, предлагающая гарантию транзакции ACID) или платформа распределённого обмена сообщениями. В приложениях с интенсивной записью (для высокой масштабируемости и пропускной способности записи) используется база данных с возможностью горизонтального масштабирования для записи (глобальные облачные базы данных). В хранилище для записи сохраняются нормализованные данные.
В качестве хранилища для чтения используется база данных NoSQL, оптимизированная для поиска (например, Apache Solr, Elasticsearch) или чтения (хранилище пар ключ-значение, хранилище документов). Во многих случаях, когда требуется SQL-запрос, используются масштабируемые для чтения базы данных SQL. В хранилище для чтения сохраняются денормализованные и оптимизированные данные.
Данные копируются из хранилища для записи в хранилище для чтения асинхронно. В результате хранилище чтения отстает от хранилища записи и имеет место Окончательная Согласованность (Eventual Consistency).
Плюсы
- Более быстрое чтение данных в микросервисах, управляемых событиями.
- Высокая доступность данных.
- Системы чтения и записи могут масштабироваться независимо.
Минусы
- Хранилище для чтения слабо согласовано (окончательная согласованность)
- Общая сложность системы увеличивается. Чрезмерная увлечённость CQRS может поставить под угрозу весь проект.
Когда использовать
- В высокомасштабируемой микросервисной архитектуре, где используется паттерн источников событий.
- В сложной модели предметной области, где для чтения данных требуется запрос в несколько хранилищ данных.
- В системах, где операции чтения и записи имеют разную нагрузку.
Когда не использовать
- В микросервисной архитектуре, где объём событий незначителен, создание моментального снимка хранилища событий для вычисления состояния объекта является лучшим выбором.
- В системах, где операции чтения и записи имеют одинаковую нагрузку.
Поддержка
Хранилища для Записи: EventStoreDB, Apache Kafka, Confluent Cloud, AWS Kinesis, Azure Event Hub, GCP Pub/Sub, Azure Cosmos DB, MongoDB, Cassandra, Amazon DynamoDB
Хранилища для Чтения: Elastic Search, Solr, Cloud Spanner, Amazon Aurora, Azure Cosmos DB, Neo4j
Фреймворки: Lagom, Akka, Spring, akkatecture, Axon, Eventuate
Подробнее:
- CQRS
- Что такое шаблон CQRS?
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍1
День восемьсот девяносто пятый. #DesignPatterns #Microservices
Паттерны в Микросервисах
4. Saga
Если вы используете микросервисную архитектуру с базой данных на микросервис, то управление согласованностью с помощью распределённых транзакций является сложной задачей. Вы не можете использовать традиционный протокол двухфазной фиксации (2PC), поскольку он либо не масштабируется (базы данных SQL), либо не поддерживается (многие базы данных NoSQL).
Вы можете использовать паттерн Saga для распределённых транзакций в микросервисной архитектуре. Saga - это старый паттерн, разработанный в 1987 году как концептуальная альтернатива длительным транзакциям в базах данных SQL. Но современный вариант этого паттерна отлично работает и для распределённой транзакции. Паттерн Saga - это последовательность локальных транзакций, в которой каждая транзакция обновляет данные в хранилище данных в рамках одного микросервиса и публикует событие или сообщение. Первая транзакция в Saga инициируется внешним запросом (событием или действием). После завершения локальной транзакции (данные сохраняются в хранилище данных, а сообщение или событие публикуются) опубликованное сообщение/событие запускает следующую локальную транзакцию в Saga (см. картинку ниже).
Если локальная транзакция терпит неудачу, Saga выполняет серию компенсирующих транзакций, которые откатывают изменения предыдущих локальных транзакций.
В основном существует два варианта координации транзакций в Saga:
- Хореография: децентрализованная координация, при которой каждый микросервис производит и прослушивает события/сообщения других микросервисов и решает, следует ли предпринять действие или нет.
- Оркестрация: централизованная координация, при которой оркестратор сообщает участвующим микросервисам, какую локальную транзакцию необходимо выполнить.
Плюсы
- Обеспечение согласованности посредством транзакций в высокомасштабируемой или слабо связанной, управляемой событиями микросервисной архитектуре.
- Обеспечение согласованности посредством транзакций в микросервисной архитектуре, где используются базы данных NoSQL без поддержки 2PC.
Минусы
- Необходимо обрабатывать временные отказы и обеспечивать идемпотентность.
- Трудно отлаживать, и сложность растёт по мере увеличения количества микросервисов.
Когда использовать
- В высокомасштабируемой, слабо связанной микросервисной архитектуре, где используются источники событий https://t.me/NetDeveloperDiary/1084.
- В системах, где используются распределённые базы данных NoSQL.
Когда не использовать
- Слабо масштабируемые транзакционные системы с базами данных SQL.
- В системах, где существует циклическая зависимость между сервисами.
Поддержка
Axon, Eventuate, Narayana
Подробнее
- Saga — распределённые транзакции
- Microservices Pattern: Saga
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
Паттерны в Микросервисах
4. Saga
Если вы используете микросервисную архитектуру с базой данных на микросервис, то управление согласованностью с помощью распределённых транзакций является сложной задачей. Вы не можете использовать традиционный протокол двухфазной фиксации (2PC), поскольку он либо не масштабируется (базы данных SQL), либо не поддерживается (многие базы данных NoSQL).
Вы можете использовать паттерн Saga для распределённых транзакций в микросервисной архитектуре. Saga - это старый паттерн, разработанный в 1987 году как концептуальная альтернатива длительным транзакциям в базах данных SQL. Но современный вариант этого паттерна отлично работает и для распределённой транзакции. Паттерн Saga - это последовательность локальных транзакций, в которой каждая транзакция обновляет данные в хранилище данных в рамках одного микросервиса и публикует событие или сообщение. Первая транзакция в Saga инициируется внешним запросом (событием или действием). После завершения локальной транзакции (данные сохраняются в хранилище данных, а сообщение или событие публикуются) опубликованное сообщение/событие запускает следующую локальную транзакцию в Saga (см. картинку ниже).
Если локальная транзакция терпит неудачу, Saga выполняет серию компенсирующих транзакций, которые откатывают изменения предыдущих локальных транзакций.
В основном существует два варианта координации транзакций в Saga:
- Хореография: децентрализованная координация, при которой каждый микросервис производит и прослушивает события/сообщения других микросервисов и решает, следует ли предпринять действие или нет.
- Оркестрация: централизованная координация, при которой оркестратор сообщает участвующим микросервисам, какую локальную транзакцию необходимо выполнить.
Плюсы
- Обеспечение согласованности посредством транзакций в высокомасштабируемой или слабо связанной, управляемой событиями микросервисной архитектуре.
- Обеспечение согласованности посредством транзакций в микросервисной архитектуре, где используются базы данных NoSQL без поддержки 2PC.
Минусы
- Необходимо обрабатывать временные отказы и обеспечивать идемпотентность.
- Трудно отлаживать, и сложность растёт по мере увеличения количества микросервисов.
Когда использовать
- В высокомасштабируемой, слабо связанной микросервисной архитектуре, где используются источники событий https://t.me/NetDeveloperDiary/1084.
- В системах, где используются распределённые базы данных NoSQL.
Когда не использовать
- Слабо масштабируемые транзакционные системы с базами данных SQL.
- В системах, где существует циклическая зависимость между сервисами.
Поддержка
Axon, Eventuate, Narayana
Подробнее
- Saga — распределённые транзакции
- Microservices Pattern: Saga
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍2
День восемьсот девяносто восьмой. #DesignPatterns #Microservices
Паттерны в Микросервисах
5. Бэкенды для Фронтендов (BFF)
В современной разработке бизнес-приложений, особенно в микросервисной архитектуре, фронтенд и серверные приложения отделены друг от друга и представляют собой отдельные сервисы. Они общаются через API или GraphQL. Если в приложении также есть клиент для мобильного, использование одного и того же бэкенд микросервиса как для веб-клиента, так и для мобильного клиента становится проблематичным. Требования к API мобильного клиента обычно отличаются от требований веб-клиента, поскольку у них другой размер экрана, способ отображения, производительность, источник энергии и пропускная способность сети. См. картинку ниже.
Паттерн Бэкенды для Фронтендов можно использовать в сценариях, где каждый тип клиента получает отдельный серверный модуль, настроенный для конкретного пользовательского интерфейса. Паттерн также обеспечивает другие преимущества, например, выступает в качестве фасада для подчинённых микросервисов, тем самым уменьшая трафик между пользовательским интерфейсом и нижестоящими микросервисами. Кроме того, в сценарии с высокой степенью защиты, когда нижестоящие микросервисы развертываются в сети DMZ (демилитаризированной зоны), BFF используется для обеспечения более высокой безопасности.
Плюсы
- Разделение проблем между сервисами. Возможность оптимизировать их для конкретного пользовательского интерфейса.
- Обеспечение более высокого уровня безопасности.
- Снижение трафика между пользовательским интерфейсом и микросервисами нижестоящего уровня.
Минусы
- Дублирование кода между сервисами.
- Размножение сервисов в случае использования множества разных пользовательских интерфейсов (например, Smart TV, Web, Mobile, Desktop).
- Требуется тщательная разработка и реализация, поскольку BFF не должны содержать никакой бизнес-логики, только логику и поведение, специфичные для клиента.
Когда использовать
- Если приложение имеет несколько пользовательских интерфейсов с разными требованиями к API.
- Если требуется дополнительный уровень между пользовательским интерфейсом и микросервисами нижестоящего уровня по соображениям безопасности.
- Если при разработке пользовательского интерфейса используются микро-интерфейсы.
Когда не использовать
- Если у приложения несколько пользовательских интерфейсов, но они используют один и тот же API.
- Если основные микросервисы не развернуты в DMZ.
Поддержка
Большинство фреймворков для бэкенда (Node.js, Spring, Django, Laravel, Flask, Play, и т.д.).
Подробнее
- Бэкенды для фронтендов
- Microservices Pattern: API Gateway / Backends for Frontends
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
Паттерны в Микросервисах
5. Бэкенды для Фронтендов (BFF)
В современной разработке бизнес-приложений, особенно в микросервисной архитектуре, фронтенд и серверные приложения отделены друг от друга и представляют собой отдельные сервисы. Они общаются через API или GraphQL. Если в приложении также есть клиент для мобильного, использование одного и того же бэкенд микросервиса как для веб-клиента, так и для мобильного клиента становится проблематичным. Требования к API мобильного клиента обычно отличаются от требований веб-клиента, поскольку у них другой размер экрана, способ отображения, производительность, источник энергии и пропускная способность сети. См. картинку ниже.
Паттерн Бэкенды для Фронтендов можно использовать в сценариях, где каждый тип клиента получает отдельный серверный модуль, настроенный для конкретного пользовательского интерфейса. Паттерн также обеспечивает другие преимущества, например, выступает в качестве фасада для подчинённых микросервисов, тем самым уменьшая трафик между пользовательским интерфейсом и нижестоящими микросервисами. Кроме того, в сценарии с высокой степенью защиты, когда нижестоящие микросервисы развертываются в сети DMZ (демилитаризированной зоны), BFF используется для обеспечения более высокой безопасности.
Плюсы
- Разделение проблем между сервисами. Возможность оптимизировать их для конкретного пользовательского интерфейса.
- Обеспечение более высокого уровня безопасности.
- Снижение трафика между пользовательским интерфейсом и микросервисами нижестоящего уровня.
Минусы
- Дублирование кода между сервисами.
- Размножение сервисов в случае использования множества разных пользовательских интерфейсов (например, Smart TV, Web, Mobile, Desktop).
- Требуется тщательная разработка и реализация, поскольку BFF не должны содержать никакой бизнес-логики, только логику и поведение, специфичные для клиента.
Когда использовать
- Если приложение имеет несколько пользовательских интерфейсов с разными требованиями к API.
- Если требуется дополнительный уровень между пользовательским интерфейсом и микросервисами нижестоящего уровня по соображениям безопасности.
- Если при разработке пользовательского интерфейса используются микро-интерфейсы.
Когда не использовать
- Если у приложения несколько пользовательских интерфейсов, но они используют один и тот же API.
- Если основные микросервисы не развернуты в DMZ.
Поддержка
Большинство фреймворков для бэкенда (Node.js, Spring, Django, Laravel, Flask, Play, и т.д.).
Подробнее
- Бэкенды для фронтендов
- Microservices Pattern: API Gateway / Backends for Frontends
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍3
День девятьсот второй. #DesignPatterns #Microservices
Паттерны в Микросервисах
6. API-шлюз (API Gateway)
В архитектуре микросервисов пользовательский интерфейс обычно соединяется с несколькими микросервисами. Если микросервисы являются слишком узкоспециализированными (FaaS), клиенту может потребоваться подключение к большому количеству микросервисов, что становится сложным и требует множества различных коммуникаций. Кроме того, сервисы, включая их API, могут развиваться. Также крупные системы обычно добавляют стандартные задачи (SSL-терминацию, аутентификацию, авторизацию, троттлинг, ведение журнала и т. д.).
Один из возможных способов решения этих проблем - использовать API-шлюз. Он располагается между клиентским приложением и внутренними микросервисами и действует как фасад. API-шлюз может работать как обратный прокси-сервер, перенаправляя клиентский запрос на соответствующий внутренний микросервис. Также может поддерживать разветвление клиентского запроса на несколько микросервисов, а затем возвращать агрегированные ответы клиенту. Кроме того, он может решать важные задачи общие для всех микросервисов. См. картинку ниже.
Достоинства
- Предлагает слабую связь между фронтендом и микросервисами на бэкенде.
- Уменьшает количество вызовов между клиентом и микросервисами.
- Повышает безопасность за счет централизованной SSL-терминации, аутентификации и авторизации.
- Централизованно управляет общими задачами, например, ведением журнала и мониторингом, троттлингом и балансировкой нагрузки.
Недостатки
- Сбой в API-шлюзе становится «единой точкой отказа» в микросервисной архитектуре.
- Увеличивает задержку из-за дополнительного сетевого вызова.
- Если не масштабировать API-шлюз, он легко может стать узким местом для всей системы.
- Увеличивает стоимость разработки и обслуживания.
Когда использовать
- В сложной микросервисной архитектуре это почти обязательно.
- В крупных системах API-шлюз является обязательным для централизации безопасности и решения общих задач.
Когда не использовать
- В частных проектах или небольших компаниях, где безопасность и централизованное управление не являются наивысшим приоритетом.
- Если количество микросервисов невелико.
Поддержка
Amazon API Gateway, Azure API Management, Ocelot, Apigee, Kong, WSO2 API Manager
Подробнее
- Использование API-шлюзов в микросервисах
- Microservices Pattern: API Gateway / Backends for Frontends
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
Паттерны в Микросервисах
6. API-шлюз (API Gateway)
В архитектуре микросервисов пользовательский интерфейс обычно соединяется с несколькими микросервисами. Если микросервисы являются слишком узкоспециализированными (FaaS), клиенту может потребоваться подключение к большому количеству микросервисов, что становится сложным и требует множества различных коммуникаций. Кроме того, сервисы, включая их API, могут развиваться. Также крупные системы обычно добавляют стандартные задачи (SSL-терминацию, аутентификацию, авторизацию, троттлинг, ведение журнала и т. д.).
Один из возможных способов решения этих проблем - использовать API-шлюз. Он располагается между клиентским приложением и внутренними микросервисами и действует как фасад. API-шлюз может работать как обратный прокси-сервер, перенаправляя клиентский запрос на соответствующий внутренний микросервис. Также может поддерживать разветвление клиентского запроса на несколько микросервисов, а затем возвращать агрегированные ответы клиенту. Кроме того, он может решать важные задачи общие для всех микросервисов. См. картинку ниже.
Достоинства
- Предлагает слабую связь между фронтендом и микросервисами на бэкенде.
- Уменьшает количество вызовов между клиентом и микросервисами.
- Повышает безопасность за счет централизованной SSL-терминации, аутентификации и авторизации.
- Централизованно управляет общими задачами, например, ведением журнала и мониторингом, троттлингом и балансировкой нагрузки.
Недостатки
- Сбой в API-шлюзе становится «единой точкой отказа» в микросервисной архитектуре.
- Увеличивает задержку из-за дополнительного сетевого вызова.
- Если не масштабировать API-шлюз, он легко может стать узким местом для всей системы.
- Увеличивает стоимость разработки и обслуживания.
Когда использовать
- В сложной микросервисной архитектуре это почти обязательно.
- В крупных системах API-шлюз является обязательным для централизации безопасности и решения общих задач.
Когда не использовать
- В частных проектах или небольших компаниях, где безопасность и централизованное управление не являются наивысшим приоритетом.
- Если количество микросервисов невелико.
Поддержка
Amazon API Gateway, Azure API Management, Ocelot, Apigee, Kong, WSO2 API Manager
Подробнее
- Использование API-шлюзов в микросервисах
- Microservices Pattern: API Gateway / Backends for Frontends
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍2
День девятьсот восьмой. #DesignPatterns #Microservices
Паттерны в Микросервисах
7. Душитель (Strangler)
Если мы хотим использовать микросервисную архитектуру в унаследованном проекте, нам необходимо перенести устаревшие или существующие монолитные приложения на микросервисы. Перемещение существующих и работающих крупных монолитных приложений на микросервисы является довольно сложной задачей, так как это может нарушить доступность приложения.
Одно из решений - использовать паттерн Душитель. Он предполагает постепенную миграцию монолитного приложения на микросервисную архитектуру путём постепенной замены определённых функций новыми микросервисами. Кроме того, новый функционал добавляется только в микросервисы, минуя устаревшее монолитное приложение. Затем настраивается фасад (API-шлюз https://t.me/NetDeveloperDiary/1106) для маршрутизации запросов между устаревшим монолитом и микросервисами. После того, как функциональность перенесена на микросервисы, фасад перехватывает клиентский запрос и направляет его на новые микросервисы. После переноса всего функционала старого монолита получается, что монолитное приложение «задушено», то есть оно выводится из эксплуатации. См. картинку ниже.
Достоинства
- Безопасная миграция монолитного приложения на микросервисы.
- Миграция и разработка нового функционала могут идти параллельно.
- Процесс миграции может иметь свой темп, весь функционал постоянно доступен клиентам.
Недостатки
- Совместное использование хранилища данных между существующим монолитным приложением и новыми микросервисами становится сложной задачей.
- Добавление фасада (API-шлюза) увеличит время отклика системы.
- Сквозное тестирование становится трудным.
Когда использовать
- Постепенная миграция большого серверного монолитного приложения на микросервисы.
Когда не использовать
- Если монолитное приложение на бэкенде небольшое, то лучше заменить его целиком.
- Если клиентский запрос к устаревшему монолитному приложению не может быть перехвачен.
Поддержка
Бэкенд фреймворки с поддержкой API-шлюзов.
Подробнее
- Паттерн Душитель
- Microservices Pattern: Strangler application
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
Паттерны в Микросервисах
7. Душитель (Strangler)
Если мы хотим использовать микросервисную архитектуру в унаследованном проекте, нам необходимо перенести устаревшие или существующие монолитные приложения на микросервисы. Перемещение существующих и работающих крупных монолитных приложений на микросервисы является довольно сложной задачей, так как это может нарушить доступность приложения.
Одно из решений - использовать паттерн Душитель. Он предполагает постепенную миграцию монолитного приложения на микросервисную архитектуру путём постепенной замены определённых функций новыми микросервисами. Кроме того, новый функционал добавляется только в микросервисы, минуя устаревшее монолитное приложение. Затем настраивается фасад (API-шлюз https://t.me/NetDeveloperDiary/1106) для маршрутизации запросов между устаревшим монолитом и микросервисами. После того, как функциональность перенесена на микросервисы, фасад перехватывает клиентский запрос и направляет его на новые микросервисы. После переноса всего функционала старого монолита получается, что монолитное приложение «задушено», то есть оно выводится из эксплуатации. См. картинку ниже.
Достоинства
- Безопасная миграция монолитного приложения на микросервисы.
- Миграция и разработка нового функционала могут идти параллельно.
- Процесс миграции может иметь свой темп, весь функционал постоянно доступен клиентам.
Недостатки
- Совместное использование хранилища данных между существующим монолитным приложением и новыми микросервисами становится сложной задачей.
- Добавление фасада (API-шлюза) увеличит время отклика системы.
- Сквозное тестирование становится трудным.
Когда использовать
- Постепенная миграция большого серверного монолитного приложения на микросервисы.
Когда не использовать
- Если монолитное приложение на бэкенде небольшое, то лучше заменить его целиком.
- Если клиентский запрос к устаревшему монолитному приложению не может быть перехвачен.
Поддержка
Бэкенд фреймворки с поддержкой API-шлюзов.
Подробнее
- Паттерн Душитель
- Microservices Pattern: Strangler application
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍1
День девятьсот пятнадцатый. #DesignPatterns #Microservices
Паттерны в Микросервисах
8. Прерыватель Цепи (Circuit Breaker)
В микросервисной архитектуре, где микросервисы обмениваются данными синхронно, один микросервис обычно вызывает другие сервисы для выполнения бизнес-логики. Вызов другого сервиса может завершиться ошибкой из-за временных сбоев (медленное сетевое соединение, тайм-ауты или временная недоступность). В таких случаях повторные вызовы могут решить проблему. Однако, если есть серьёзная проблема (полный отказ микросервиса), сервис будет недоступен в течение более длительного времени. Выполнение повторных попыток бессмысленно и впустую тратит драгоценные ресурсы (блокируется поток, тратятся циклы ЦП). Кроме того, отказ одного сервиса может привести к каскадным сбоям во всем приложении. В таких сценариях немедленный отказ - лучший подход.
В таких случаях на помощь может прийти паттерн «Прерыватель Цепи». Микросервис запрашивает другой сервис через прокси-сервер, который работает аналогично электрическому выключателю. Прокси-сервер должен подсчитывать количество недавних сбоев и использовать эти данные, чтобы решить, разрешить ли выполнение операции или просто немедленно вернуть исключение. См. картинку ниже.
Прерыватель цепи может находиться в следующих трёх состояниях:
- Замкнут: прерыватель цепи направляет запросы в микросервис и подсчитывает количество сбоев за заданный период времени. Если количество отказов за определённый период времени превышает пороговое значение, он отключается и переходит в разомкнутое состояние.
- Разомкнут: запрос от микросервиса немедленно завершается ошибкой, и возвращается исключение. По истечении времени ожидания прерыватель цепи переходит в полузамкнутое состояние.
- Полузамкнут: только ограниченное количество запросов от микросервиса может пройти и вызвать выполнение операции. Если эти запросы выполнены успешно, прерыватель цепи переходит в замкнутое состояние. Если какой-либо запрос терпит неудачу, прерыватель цепи переходит в разомкнутое состояние.
Преимущества
- Повышение отказоустойчивости микросервисной архитектуры.
- Прерывание каскадной передачи сбоев в другие микросервисы.
Недостатки
- Нужна сложная обработка исключений.
- Дополнительное логирование и мониторинг.
- Необходимость поддержки ручного сброса.
Когда использовать
- В тесно связанной микросервисной архитектуре, где микросервисы обмениваются данными синхронно.
- Если один микросервис зависит от нескольких других микросервисов.
Когда не использовать
- Слабо связанная микросервисная архитектура, управляемая событиями.
- Если микросервис не зависит от других микросервисов.
Поддержка
API Gateway, Service Mesh, various Circuit Breaker Libraries (Hystrix, Reselience4J, Polly.
Подробнее
- Паттерн Прерыватель Цепи
- Microservices Pattern: Circuit Breaker
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
Паттерны в Микросервисах
8. Прерыватель Цепи (Circuit Breaker)
В микросервисной архитектуре, где микросервисы обмениваются данными синхронно, один микросервис обычно вызывает другие сервисы для выполнения бизнес-логики. Вызов другого сервиса может завершиться ошибкой из-за временных сбоев (медленное сетевое соединение, тайм-ауты или временная недоступность). В таких случаях повторные вызовы могут решить проблему. Однако, если есть серьёзная проблема (полный отказ микросервиса), сервис будет недоступен в течение более длительного времени. Выполнение повторных попыток бессмысленно и впустую тратит драгоценные ресурсы (блокируется поток, тратятся циклы ЦП). Кроме того, отказ одного сервиса может привести к каскадным сбоям во всем приложении. В таких сценариях немедленный отказ - лучший подход.
В таких случаях на помощь может прийти паттерн «Прерыватель Цепи». Микросервис запрашивает другой сервис через прокси-сервер, который работает аналогично электрическому выключателю. Прокси-сервер должен подсчитывать количество недавних сбоев и использовать эти данные, чтобы решить, разрешить ли выполнение операции или просто немедленно вернуть исключение. См. картинку ниже.
Прерыватель цепи может находиться в следующих трёх состояниях:
- Замкнут: прерыватель цепи направляет запросы в микросервис и подсчитывает количество сбоев за заданный период времени. Если количество отказов за определённый период времени превышает пороговое значение, он отключается и переходит в разомкнутое состояние.
- Разомкнут: запрос от микросервиса немедленно завершается ошибкой, и возвращается исключение. По истечении времени ожидания прерыватель цепи переходит в полузамкнутое состояние.
- Полузамкнут: только ограниченное количество запросов от микросервиса может пройти и вызвать выполнение операции. Если эти запросы выполнены успешно, прерыватель цепи переходит в замкнутое состояние. Если какой-либо запрос терпит неудачу, прерыватель цепи переходит в разомкнутое состояние.
Преимущества
- Повышение отказоустойчивости микросервисной архитектуры.
- Прерывание каскадной передачи сбоев в другие микросервисы.
Недостатки
- Нужна сложная обработка исключений.
- Дополнительное логирование и мониторинг.
- Необходимость поддержки ручного сброса.
Когда использовать
- В тесно связанной микросервисной архитектуре, где микросервисы обмениваются данными синхронно.
- Если один микросервис зависит от нескольких других микросервисов.
Когда не использовать
- Слабо связанная микросервисная архитектура, управляемая событиями.
- Если микросервис не зависит от других микросервисов.
Поддержка
API Gateway, Service Mesh, various Circuit Breaker Libraries (Hystrix, Reselience4J, Polly.
Подробнее
- Паттерн Прерыватель Цепи
- Microservices Pattern: Circuit Breaker
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍2
День девятьсот двадцать второй. #DesignPatterns #Microservices
Паттерны в Микросервисах
9. Внешняя конфигурация
Каждое бизнес-приложение имеет множество параметров конфигурации для различной инфраструктуры (например, база данных, сеть, адреса подключенных служб, учетные данные, путь к сертификату). Кроме того, в корпоративной среде приложение обычно развёртывается в различных средах выполнения (локальной, тестовой, производственной). Один из способов добиться этого - использовать конфигурацию внутри каждого приложения в каждом случае, что является фатальной плохой практикой. Это может привести к серьёзным угрозам безопасности, поскольку производственные учётные данные могут быть легко скомпрометированы. Кроме того, при любом изменении параметра конфигурации необходимо пересобрать приложение. Это ещё более важно в микросервисной архитектуре, поскольку у нас потенциально есть сотни сервисов.
Лучшим подходом является экстернализация всех конфигураций. В результате процесс сборки отделён от среды выполнения. Кроме того, это сводит к минимуму риск безопасности, поскольку файл конфигурации используется только во время выполнения или через переменные среды.
Плюсы
- Рабочие конфигурации не являются частью кодовой базы и, таким образом, сводят к минимуму уязвимость системы безопасности.
- Параметры конфигурации можно изменить без новой сборки.
Минусы
- Нам нужно выбрать фреймворк, поддерживающий внешнюю конфигурацию.
Когда использовать
- Любое серьезное производственное приложение должно использовать внешнюю конфигурацию.
Когда не использовать
- В тестовых приложениях.
Включение технологий
Практически все современные фреймворки корпоративного уровня поддерживают внешнюю конфигурацию.
Подробнее
- Microservices Pattern: Externalized configuration
- Build Once, Run Anywhere: Externalize Your Configuration
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
Паттерны в Микросервисах
9. Внешняя конфигурация
Каждое бизнес-приложение имеет множество параметров конфигурации для различной инфраструктуры (например, база данных, сеть, адреса подключенных служб, учетные данные, путь к сертификату). Кроме того, в корпоративной среде приложение обычно развёртывается в различных средах выполнения (локальной, тестовой, производственной). Один из способов добиться этого - использовать конфигурацию внутри каждого приложения в каждом случае, что является фатальной плохой практикой. Это может привести к серьёзным угрозам безопасности, поскольку производственные учётные данные могут быть легко скомпрометированы. Кроме того, при любом изменении параметра конфигурации необходимо пересобрать приложение. Это ещё более важно в микросервисной архитектуре, поскольку у нас потенциально есть сотни сервисов.
Лучшим подходом является экстернализация всех конфигураций. В результате процесс сборки отделён от среды выполнения. Кроме того, это сводит к минимуму риск безопасности, поскольку файл конфигурации используется только во время выполнения или через переменные среды.
Плюсы
- Рабочие конфигурации не являются частью кодовой базы и, таким образом, сводят к минимуму уязвимость системы безопасности.
- Параметры конфигурации можно изменить без новой сборки.
Минусы
- Нам нужно выбрать фреймворк, поддерживающий внешнюю конфигурацию.
Когда использовать
- Любое серьезное производственное приложение должно использовать внешнюю конфигурацию.
Когда не использовать
- В тестовых приложениях.
Включение технологий
Практически все современные фреймворки корпоративного уровня поддерживают внешнюю конфигурацию.
Подробнее
- Microservices Pattern: Externalized configuration
- Build Once, Run Anywhere: Externalize Your Configuration
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍1
День девятьсот тридцатый. #DesignPatterns #Microservices
Паттерны в Микросервисах
10. Тестирование Контрактов, Ориентированных на Потребителя
В микросервисной архитектуре зачастую разные микросервисы разрабатываются отдельными группами. Эти микросервисы работают вместе для выполнения бизнес-требований (например, запроса клиента) и взаимодействуют друг с другом синхронно или асинхронно. Интеграционное тестирование микросервиса со стороны потребителя - непростая задача. Обычно в таких сценариях используется объект-имитация для более быстрой и дешёвой реализации тестов. Но он зачастую не полностью представляет собой настоящий микросервис провайдера. Кроме того, если микросервис провайдера изменяет свой API или сообщение, объект-имитация не может это отразить. Другой вариант - провести сквозное тестирование. Хотя сквозное тестирование является обязательным перед выпуском продукта, оно хрупкое, медленное, дорогое и не может заменить интеграционное тестирование.
В этом случае нам может помочь тестирование контрактов, ориентированных на потребителя (Consumer-Driven Contract Testing). При этом группа, разрабатывающая микросервис потребителя, пишет набор тестов, содержащий его запрос и ожидаемый ответ (для синхронного взаимодействия) или ожидаемые сообщения (для асинхронного взаимодействия) под конкретный микросервис провайдера. Эти наборы тестов называются явными контрактами. Для микросервиса провайдера все наборы тестов из контракта его потребителей добавляются в его набор автоматизированных тестов. Когда выполняется автоматизированное тестирование конкретного микросервиса провайдера, запускаются его собственные тесты и тесты по контракту. Таким образом, проверка контракта может помочь в автоматическом поддержании целостности микросервисной коммуникации.
Плюсы
- Если провайдер неожиданно изменяет API или сообщение, это автоматически обнаруживается за короткое время.
- Меньше неожиданностей и больше надёжности, особенно в корпоративном приложении, содержащем множество микросервисов.
- Повышается автономность команд.
Минусы
- Требуется дополнительная работа по разработке и интеграции контрактных тестов в микросервис провайдера, поскольку разные команды могут использовать разные инструменты тестирования.
- Если проверка контракта не соответствует реальному сценарию потребителя сервиса, это может привести к сбою в производственной среде.
Когда использовать
- В крупномасштабных корпоративных бизнес-приложениях, где, как правило, разные команды разрабатывают разные сервисы.
Когда не использовать
- В относительно более простых и небольших приложениях, в которых одна команда разрабатывает все микросервисы.
- Если микросервисы провайдера относительно стабильны и не находятся в активной разработке.
Поддержка
Pact, Postman, Spring Cloud Contract
Подробнее
- Microservices Pattern: Service Integration Contract Test
- Consumer-Driven Contracts: A Service Evolution Pattern
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
Паттерны в Микросервисах
10. Тестирование Контрактов, Ориентированных на Потребителя
В микросервисной архитектуре зачастую разные микросервисы разрабатываются отдельными группами. Эти микросервисы работают вместе для выполнения бизнес-требований (например, запроса клиента) и взаимодействуют друг с другом синхронно или асинхронно. Интеграционное тестирование микросервиса со стороны потребителя - непростая задача. Обычно в таких сценариях используется объект-имитация для более быстрой и дешёвой реализации тестов. Но он зачастую не полностью представляет собой настоящий микросервис провайдера. Кроме того, если микросервис провайдера изменяет свой API или сообщение, объект-имитация не может это отразить. Другой вариант - провести сквозное тестирование. Хотя сквозное тестирование является обязательным перед выпуском продукта, оно хрупкое, медленное, дорогое и не может заменить интеграционное тестирование.
В этом случае нам может помочь тестирование контрактов, ориентированных на потребителя (Consumer-Driven Contract Testing). При этом группа, разрабатывающая микросервис потребителя, пишет набор тестов, содержащий его запрос и ожидаемый ответ (для синхронного взаимодействия) или ожидаемые сообщения (для асинхронного взаимодействия) под конкретный микросервис провайдера. Эти наборы тестов называются явными контрактами. Для микросервиса провайдера все наборы тестов из контракта его потребителей добавляются в его набор автоматизированных тестов. Когда выполняется автоматизированное тестирование конкретного микросервиса провайдера, запускаются его собственные тесты и тесты по контракту. Таким образом, проверка контракта может помочь в автоматическом поддержании целостности микросервисной коммуникации.
Плюсы
- Если провайдер неожиданно изменяет API или сообщение, это автоматически обнаруживается за короткое время.
- Меньше неожиданностей и больше надёжности, особенно в корпоративном приложении, содержащем множество микросервисов.
- Повышается автономность команд.
Минусы
- Требуется дополнительная работа по разработке и интеграции контрактных тестов в микросервис провайдера, поскольку разные команды могут использовать разные инструменты тестирования.
- Если проверка контракта не соответствует реальному сценарию потребителя сервиса, это может привести к сбою в производственной среде.
Когда использовать
- В крупномасштабных корпоративных бизнес-приложениях, где, как правило, разные команды разрабатывают разные сервисы.
Когда не использовать
- В относительно более простых и небольших приложениях, в которых одна команда разрабатывает все микросервисы.
- Если микросервисы провайдера относительно стабильны и не находятся в активной разработке.
Поддержка
Pact, Postman, Spring Cloud Contract
Подробнее
- Microservices Pattern: Service Integration Contract Test
- Consumer-Driven Contracts: A Service Evolution Pattern
Источник: https://towardsdatascience.com/microservice-architecture-and-its-10-most-important-design-patterns-824952d7fa41
👍2
День 1191.
Подборка тегов, используемых в постах на канале, чтобы облегчить поиск. Не могу гарантировать, что все 1190 постов идеально и корректно помечены тегами, но всё-таки, эта подборка должна помочь.
Общие
Эти посты на совершенно разные темы, помечены этими тегами только с целью различать общую направленность поста.
#ЗаметкиНаПолях – технические посты. Краткие описания теории, особенности языка C# и платформы .NET, примеры кода, и т.п.
#Шпаргалка - примеры кода, команды для утилит и т.п.
#Юмор – шутки, комиксы и просто весёлые тексты или ссылки на видео.
#Оффтоп – всё прочее.
Специализированные
Эти теги более тематические, выделяют основную тему поста.
#Карьера – советы по повышению продуктивности, карьерному росту, прохождению собеседований и т.п.
#Книги – обзоры книг, которые (чаще всего) я лично прочитал, либо ещё нет, но советую прочитать.
#Курсы – обзоры и ссылки на онлайн курсы.
#МоиИнструменты – различные программы, утилиты и расширения IDE, которые я использую в работе.
#ЧтоНовенького – новости из мира .NET.
Узкоспециализированные
Эти теги относятся к определённой узкой теме.
#AsyncTips – серия постов из книги Стивена Клири “Конкурентность в C#”
#AsyncAwaitFAQ – серия постов “Самые Частые Ошибки при Работе с async/await.”
#BestPractices – советы по лучшим практикам, паттернам разработки.
#DesignPatterns – всё о паттернах проектирования, SOLID, IDEALS и т.п.
#DotNetAZ – серия постов с описанием терминов из мира .NET.
#GC – серия постов “Топ Вопросов о Памяти в .NET.” от Конрада Кокосы.
#MoreEffectiveCSharp – серия постов из книги Билла Вагнера “More Effective C#”.
#Testing – всё о тестировании кода.
#TipsAndTricks – советы и трюки, в основном по функционалу Visual Studio.
#Quiz - опросы в виде викторины.
#97Вещей – серия постов из книги “97 Вещей, Которые Должен Знать Каждый Программист”.
#ВопросыНаСобеседовании – тег говорит сам за себя, самые часто задаваемые вопросы на собеседовании по C#, ASP.NET и .NET.
#ЗадачиНаСобеседовании – похоже на вопросы, но здесь больше приводятся практические задачи. Чаще всего это 2 поста: собственно задача и ответ с разбором.
#КакСтатьСеньором – серия постов «Как Стать Сеньором» с советами о продвижении по карьерной лестнице.
Помимо этого, можно просто воспользоваться поиском по постам и попробовать найти то, что вам нужно.
Подборка тегов, используемых в постах на канале, чтобы облегчить поиск. Не могу гарантировать, что все 1190 постов идеально и корректно помечены тегами, но всё-таки, эта подборка должна помочь.
Общие
Эти посты на совершенно разные темы, помечены этими тегами только с целью различать общую направленность поста.
#ЗаметкиНаПолях – технические посты. Краткие описания теории, особенности языка C# и платформы .NET, примеры кода, и т.п.
#Шпаргалка - примеры кода, команды для утилит и т.п.
#Юмор – шутки, комиксы и просто весёлые тексты или ссылки на видео.
#Оффтоп – всё прочее.
Специализированные
Эти теги более тематические, выделяют основную тему поста.
#Карьера – советы по повышению продуктивности, карьерному росту, прохождению собеседований и т.п.
#Книги – обзоры книг, которые (чаще всего) я лично прочитал, либо ещё нет, но советую прочитать.
#Курсы – обзоры и ссылки на онлайн курсы.
#МоиИнструменты – различные программы, утилиты и расширения IDE, которые я использую в работе.
#ЧтоНовенького – новости из мира .NET.
Узкоспециализированные
Эти теги относятся к определённой узкой теме.
#AsyncTips – серия постов из книги Стивена Клири “Конкурентность в C#”
#AsyncAwaitFAQ – серия постов “Самые Частые Ошибки при Работе с async/await.”
#BestPractices – советы по лучшим практикам, паттернам разработки.
#DesignPatterns – всё о паттернах проектирования, SOLID, IDEALS и т.п.
#DotNetAZ – серия постов с описанием терминов из мира .NET.
#GC – серия постов “Топ Вопросов о Памяти в .NET.” от Конрада Кокосы.
#MoreEffectiveCSharp – серия постов из книги Билла Вагнера “More Effective C#”.
#Testing – всё о тестировании кода.
#TipsAndTricks – советы и трюки, в основном по функционалу Visual Studio.
#Quiz - опросы в виде викторины.
#97Вещей – серия постов из книги “97 Вещей, Которые Должен Знать Каждый Программист”.
#ВопросыНаСобеседовании – тег говорит сам за себя, самые часто задаваемые вопросы на собеседовании по C#, ASP.NET и .NET.
#ЗадачиНаСобеседовании – похоже на вопросы, но здесь больше приводятся практические задачи. Чаще всего это 2 поста: собственно задача и ответ с разбором.
#КакСтатьСеньором – серия постов «Как Стать Сеньором» с советами о продвижении по карьерной лестнице.
Помимо этого, можно просто воспользоваться поиском по постам и попробовать найти то, что вам нужно.
1👍59👎1
День 1216. #ЗаметкиНаПолях #DesignPatterns
Абстракции в Разработке ПО. Начало
Разработчики ПО имеют дело с абстракциями каждый день. Но что такое абстракция?
Википедия определяет абстракцию, как…
Процесс удаления физических, пространственных или временных деталей или атрибутов при изучении объектов или систем, чтобы сосредоточить внимание на более важных деталях.
Абстракция, вообще говоря, является фундаментальной концепцией в компьютерных науках и разработке ПО. Процесс абстрагирования также можно назвать моделированием. Модели также можно считать типами абстракций по их обобщению аспектов реальности.
Когда мы говорим об абстрагировании, мы обычно подразумеваем процесс моделирования путём обобщения и сосредоточения внимания на важных (в данном контексте) деталях, отбрасывая при этом менее важные детали. Когда мы говорим об абстракции, мы имеем в виду артефакты, созданные в процессе моделирования или абстрагирования. Далее поговорим об этих артефактах.
Абстракции в C# и .NET
Многие языки программирования, такие как C#, определяют абстракцию как часть своей системы типов. В рекомендациях по проектированию .NET Framework есть несколько полезных рекомендаций по правильному использованию абстракций (абстрактных типов и интерфейсов), которые включают очень простое определение:
Абстракция — это тип, описывающий контракт, но не обеспечивающий полную реализацию контракта. Абстракции обычно реализуются как абстрактные классы или интерфейсы...
Альтернативой абстракции в C# является конкретный тип. В C# можно изучить любой тип и, на основании возможности создания его непосредственного экземпляра, определить, является ли этот тип абстрактным или конкретным.
Качества абстракций
Некоторые абстракции более полезны, чем другие. Согласно цитате статистика Джорджа Бокса: «Все модели ошибочны, но некоторые из них полезны.» Мы можем посмотреть на тип в C# и определить, является ли он абстрактным, но он всё равно может быть не очень полезной абстракцией. Хорошие абстракции обладают определёнными свойствами, многие из которых описаны принципами SOLID.
Хорошие абстракции не зависят от деталей
Принцип инверсии зависимостей (DIP) гласит: «Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций».
Обычно мы можем посмотреть на абстракцию изолированно и определить, следует ли она этому принципу:
Хороший интерфейс не должен ограничивать детали своей реализации. Интерфейсы определяют, что должно произойти; реализации определяют, как это сделать.
Мы можем заменить эту плохую абстракцию, следуя DIP, устранив зависимость от низкоуровневых деталей в определении интерфейса:
Продолжение следует…
Источник: https://ardalis.com/what-are-abstractions-in-software-development/
Автор оригинала: Steve “Ardalis” Smith
Абстракции в Разработке ПО. Начало
Разработчики ПО имеют дело с абстракциями каждый день. Но что такое абстракция?
Википедия определяет абстракцию, как…
Процесс удаления физических, пространственных или временных деталей или атрибутов при изучении объектов или систем, чтобы сосредоточить внимание на более важных деталях.
Абстракция, вообще говоря, является фундаментальной концепцией в компьютерных науках и разработке ПО. Процесс абстрагирования также можно назвать моделированием. Модели также можно считать типами абстракций по их обобщению аспектов реальности.
Когда мы говорим об абстрагировании, мы обычно подразумеваем процесс моделирования путём обобщения и сосредоточения внимания на важных (в данном контексте) деталях, отбрасывая при этом менее важные детали. Когда мы говорим об абстракции, мы имеем в виду артефакты, созданные в процессе моделирования или абстрагирования. Далее поговорим об этих артефактах.
Абстракции в C# и .NET
Многие языки программирования, такие как C#, определяют абстракцию как часть своей системы типов. В рекомендациях по проектированию .NET Framework есть несколько полезных рекомендаций по правильному использованию абстракций (абстрактных типов и интерфейсов), которые включают очень простое определение:
Абстракция — это тип, описывающий контракт, но не обеспечивающий полную реализацию контракта. Абстракции обычно реализуются как абстрактные классы или интерфейсы...
Альтернативой абстракции в C# является конкретный тип. В C# можно изучить любой тип и, на основании возможности создания его непосредственного экземпляра, определить, является ли этот тип абстрактным или конкретным.
Качества абстракций
Некоторые абстракции более полезны, чем другие. Согласно цитате статистика Джорджа Бокса: «Все модели ошибочны, но некоторые из них полезны.» Мы можем посмотреть на тип в C# и определить, является ли он абстрактным, но он всё равно может быть не очень полезной абстракцией. Хорошие абстракции обладают определёнными свойствами, многие из которых описаны принципами SOLID.
Хорошие абстракции не зависят от деталей
Принцип инверсии зависимостей (DIP) гласит: «Абстракции не должны зависеть от деталей. Детали должны зависеть от абстракций».
Обычно мы можем посмотреть на абстракцию изолированно и определить, следует ли она этому принципу:
public interface IOrderDataAccessПо определению, это абстракция. Вы не можете создать экземпляр этого типа; .NET считает его абстрактным. Он предоставляет модель для работы с данными, предположительно для получения информации о заказах. Но он явно не следуют DIP, потому что зависит от низкоуровневых деталей (очевидно, будет использоваться только для запросов к базам данных SQL)
{
SqlDataReader ListOrders();
}
Хороший интерфейс не должен ограничивать детали своей реализации. Интерфейсы определяют, что должно произойти; реализации определяют, как это сделать.
Мы можем заменить эту плохую абстракцию, следуя DIP, устранив зависимость от низкоуровневых деталей в определении интерфейса:
public interface IOrderDataAccessОбратите внимание, что вы можете легко реализовать этот интерфейс, используя любую реализацию (базу данных, файлы, веб-API, в памяти, что угодно). Это прямой результат того, что он следует принципу инверсии зависимостей.
{
IEnumerable<Order> ListOrders();
}
Продолжение следует…
Источник: https://ardalis.com/what-are-abstractions-in-software-development/
Автор оригинала: Steve “Ardalis” Smith
👍21
День 1217. #ЗаметкиНаПолях #DesignPatterns
Абстракции в Разработке ПО. Продолжение
Начало
Хорошие абстракции сфокусированы
Ещё два принципа SOLID помогают нам писать более качественные абстракции:
- Единственной обязанности (SRP)
- Разделения интерфейса (ISP)
SRP обычно применяется к классам, но помните, что когда класс реализует интерфейс, он должен реализовать его весь (если он этого не делает, то он нарушает другой принцип SOLID, принцип подстановки Лисков). Таким образом, абстракции, не соответствующие SRP, приводят к классам, не соответствующим SRP.
Один из способов «сфокусировать» интерфейс — посмотреть, как он потребляется. ISP говорит, что классы «не должны зависеть от методов, которые они не используют». Если у вас есть большие интерфейсы с множеством методов, вполне вероятно, что некоторым клиентам потребуется только их подмножество, и, таким образом, нарушится ISP.
Хорошие абстракции стабильны
Есть несколько принципов, которые подводят нас к выводу, что хорошие абстракции должны быть стабильными. Стабильный в этом контексте означает, что их трудно изменить, и, как следствие, они редко меняются.
Принцип открытости-закрытости (OCP) гласит, что программные конструкции должны быть открыты для расширения, но закрыты для модификации. Абстракции предназначены для расширения за счёт их конкретных реализаций, которые не влияют на саму абстракцию. Однако иногда абстракции в системе требуют частых обновлений для поддержки новых требований. Каждое изменение абстракции влияет на все её реализации, что может иметь огромные волновые эффекты в приложении. Хорошие абстракции должны меняться редко, если вообще когда-либо.
Если вы обнаружите, что ваш дизайн требует регулярного обновления определённых абстракций, ищите лучший дизайн.
Принцип стабильных зависимостей (SDP) гласит, что зависимости между пакетами должны быть направлены в сторону более стабильных пакетов. То есть пакет должен зависеть только от более стабильных пакетов, чем он сам. Аналогично принцип стабильных абстракций предполагает, что самые стабильные пакеты должны быть самыми абстрактными. То есть абстрактность пакета должна меняться пропорционально его стабильности.
Таким образом, абстракции вашего приложения в идеале должны быть упакованы вместе в пакет, который более стабилен, чем его потребители. Следование этим (и другим) принципам приведёт вас к созданию систем с использованием чего-то близкого к чистой архитектуре или её разновидностям (порты и адаптеры, hexagonal, onion, и т. д.).
Также стоит отметить, что эти принципы не являются чисто субъективными. Для пакета можно рассчитать такие вещи, как стабильность и абстрактность. Нестабильность (Instability) определяется на основании входящих (или «афферентных»
Абстрактность пакета можно рассчитать, используя аналогичное соотношение абстрактных и конкретных классов:
Такие инструменты, как NDepend, могут быстро рассчитать стабильность и абстрактность для любого приложения .NET.
Окончание следует…
Источник: https://ardalis.com/what-are-abstractions-in-software-development/
Автор оригинала: Steve “Ardalis” Smith
Абстракции в Разработке ПО. Продолжение
Начало
Хорошие абстракции сфокусированы
Ещё два принципа SOLID помогают нам писать более качественные абстракции:
- Единственной обязанности (SRP)
- Разделения интерфейса (ISP)
SRP обычно применяется к классам, но помните, что когда класс реализует интерфейс, он должен реализовать его весь (если он этого не делает, то он нарушает другой принцип SOLID, принцип подстановки Лисков). Таким образом, абстракции, не соответствующие SRP, приводят к классам, не соответствующим SRP.
Один из способов «сфокусировать» интерфейс — посмотреть, как он потребляется. ISP говорит, что классы «не должны зависеть от методов, которые они не используют». Если у вас есть большие интерфейсы с множеством методов, вполне вероятно, что некоторым клиентам потребуется только их подмножество, и, таким образом, нарушится ISP.
Хорошие абстракции стабильны
Есть несколько принципов, которые подводят нас к выводу, что хорошие абстракции должны быть стабильными. Стабильный в этом контексте означает, что их трудно изменить, и, как следствие, они редко меняются.
Принцип открытости-закрытости (OCP) гласит, что программные конструкции должны быть открыты для расширения, но закрыты для модификации. Абстракции предназначены для расширения за счёт их конкретных реализаций, которые не влияют на саму абстракцию. Однако иногда абстракции в системе требуют частых обновлений для поддержки новых требований. Каждое изменение абстракции влияет на все её реализации, что может иметь огромные волновые эффекты в приложении. Хорошие абстракции должны меняться редко, если вообще когда-либо.
Если вы обнаружите, что ваш дизайн требует регулярного обновления определённых абстракций, ищите лучший дизайн.
Принцип стабильных зависимостей (SDP) гласит, что зависимости между пакетами должны быть направлены в сторону более стабильных пакетов. То есть пакет должен зависеть только от более стабильных пакетов, чем он сам. Аналогично принцип стабильных абстракций предполагает, что самые стабильные пакеты должны быть самыми абстрактными. То есть абстрактность пакета должна меняться пропорционально его стабильности.
Таким образом, абстракции вашего приложения в идеале должны быть упакованы вместе в пакет, который более стабилен, чем его потребители. Следование этим (и другим) принципам приведёт вас к созданию систем с использованием чего-то близкого к чистой архитектуре или её разновидностям (порты и адаптеры, hexagonal, onion, и т. д.).
Также стоит отметить, что эти принципы не являются чисто субъективными. Для пакета можно рассчитать такие вещи, как стабильность и абстрактность. Нестабильность (Instability) определяется на основании входящих (или «афферентных»
Ca
) и исходящих («эфферентных» Ce
) зависимостей:I = (Ce / (Ca + Ce))Компонент, у которого нет исходящих зависимостей (он ни от чего не зависит), полностью стабилен; нестабильность = 0. Компонент, который зависит от многих других компонентов (и, возможно, не имеет компонентов, зависящих от него, что характерно, например, для многих точек входа приложений), будет иметь нестабильность, равную 1 или близкую к ней.
Абстрактность пакета можно рассчитать, используя аналогичное соотношение абстрактных и конкретных классов:
A = Сумма(абстрактные классы)/Сумма(абстрактные + конкретные классы)В C# к абстрактным классам надо добавить и интерфейсы.
Такие инструменты, как NDepend, могут быстро рассчитать стабильность и абстрактность для любого приложения .NET.
Окончание следует…
Источник: https://ardalis.com/what-are-abstractions-in-software-development/
Автор оригинала: Steve “Ardalis” Smith
👍11
День 1218. #ЗаметкиНаПолях #DesignPatterns
Абстракции в Разработке ПО. Окончание
Начало
Продолжение
Запутанные (или откровенно ложные) правила абстракций
Есть некоторые «правила» абстракций, которые могут привести к путанице, если их вырвать из контекста, хотя изначальный их посыл правильный.
1. Интерфейсы — это не абстракции
Это правило Марка Симэна, автора книги «Внедрение зависимостей на платформе .NET». Марк описывает множество причин, по которым интерфейс может быть плохой абстракцией. Однако согласно всем определениям, которые мы рассмотрели в первой части, интерфейсы таки являются абстракциями. Просто некоторые из них - плохие абстракции. Значит правило стоит уточнить:
«Интерфейсы не всегда являются хорошими абстракциями»
2. Интерфейсы с одной реализацией не являются абстракциями
Это утверждение Владимира Хорикова, автора книги «Принципы юнит-тестирования» и курсов на Pluralsight. Аналогично сказанному выше, это правило стоит уточнить:
«Интерфейсы с одной реализацией не являются хорошими абстракциями».
Смысл этих правил в том, что вы должны иметь возможность взглянуть на интерфейс и определить, является ли он хорошей абстракцией (или, по крайней мере, демонстрирует ли он описанные выше проблемы), не видя никакого другого кода. Одна из проблем с этим заключается в том, что требуются знания реализаций интерфейса, чтобы сделать о нём оценочное суждение. А иногда вы вообще не можете быть уверены, сколько всего реализаций интерфейса существует, если он включён в пакет и может использоваться в других проектах.
Вот отрывок из книги Хорикова:
«Настоящие абстракции обнаруживаются, а не изобретаются. Открытие по определению происходит постфактум, когда абстракция уже существует, но не имеет чёткого определения в коде».
Вернёмся к первоначальному определению абстрагирования, как процесса построения полезной модели. Владимир говорит, что часто разработчики слишком быстро переходят к созданию абстракций вместо того, чтобы посмотреть, как эволюционирует их модель, а затем определять в ней полезные абстракции. Это своего рода преждевременная оптимизация.
Но ничто из этого не говорит о том, являются ли интерфейсы абстракциями или даже хорошими абстракциями, исходя из количества их реализаций.
В лучшем случае мы можем сказать:
Интерфейсы с единственной реализацией в рабочем коде — это код с запашком (code smell), потому что они могут быть плохими абстракциями из-за слишком тесной связи с их единственной реализацией.
При этом запахи кода не обязательно говорят о плохом или неправильном коде, просто их стоит изучить немного глубже, потому что они могут быть симптомом проблемы в дизайне ПО. Возможно, интерфейс был создан преждевременно, либо вручную, либо с помощью скаффолдинга, и тесно связан с деталями реализации единственного класса, в настоящее время выполняющего эту работу в системе. Знание того, что существует отношение 1:1 между интерфейсом и его реализацией, может помочь вам изучить дизайн и увидеть, есть ли с ним какие-либо проблемы (например, описанные выше, связанные с SOLID, стабильностью и т. д.). В этом смысле это может быть полезной эвристикой при оценке кода, но в идеале стоит оставить в системе все интерфейсы, которые имеют только одну реализацию, но которые на самом деле являются хорошими абстракциями.
Итого
Существует несколько определений абстракций. Некоторые аспекты могут оцениваться как истинные/ложные, например, является ли тип в C# абстрактным или конкретным. Другим может потребоваться некоторая субъективная оценка, чтобы определить, является ли данная абстракция хорошей, подлинной, полезной и т. д. А некоторые правила, претендующие на оценку того, является ли что-то абстракцией или нет, на самом деле просто говорят о том, является ли эта вещь хорошей абстракцией (или нет).
Источник: https://ardalis.com/what-are-abstractions-in-software-development/
Автор оригинала: Steve “Ardalis” Smith
Абстракции в Разработке ПО. Окончание
Начало
Продолжение
Запутанные (или откровенно ложные) правила абстракций
Есть некоторые «правила» абстракций, которые могут привести к путанице, если их вырвать из контекста, хотя изначальный их посыл правильный.
1. Интерфейсы — это не абстракции
Это правило Марка Симэна, автора книги «Внедрение зависимостей на платформе .NET». Марк описывает множество причин, по которым интерфейс может быть плохой абстракцией. Однако согласно всем определениям, которые мы рассмотрели в первой части, интерфейсы таки являются абстракциями. Просто некоторые из них - плохие абстракции. Значит правило стоит уточнить:
«Интерфейсы не всегда являются хорошими абстракциями»
2. Интерфейсы с одной реализацией не являются абстракциями
Это утверждение Владимира Хорикова, автора книги «Принципы юнит-тестирования» и курсов на Pluralsight. Аналогично сказанному выше, это правило стоит уточнить:
«Интерфейсы с одной реализацией не являются хорошими абстракциями».
Смысл этих правил в том, что вы должны иметь возможность взглянуть на интерфейс и определить, является ли он хорошей абстракцией (или, по крайней мере, демонстрирует ли он описанные выше проблемы), не видя никакого другого кода. Одна из проблем с этим заключается в том, что требуются знания реализаций интерфейса, чтобы сделать о нём оценочное суждение. А иногда вы вообще не можете быть уверены, сколько всего реализаций интерфейса существует, если он включён в пакет и может использоваться в других проектах.
Вот отрывок из книги Хорикова:
«Настоящие абстракции обнаруживаются, а не изобретаются. Открытие по определению происходит постфактум, когда абстракция уже существует, но не имеет чёткого определения в коде».
Вернёмся к первоначальному определению абстрагирования, как процесса построения полезной модели. Владимир говорит, что часто разработчики слишком быстро переходят к созданию абстракций вместо того, чтобы посмотреть, как эволюционирует их модель, а затем определять в ней полезные абстракции. Это своего рода преждевременная оптимизация.
Но ничто из этого не говорит о том, являются ли интерфейсы абстракциями или даже хорошими абстракциями, исходя из количества их реализаций.
В лучшем случае мы можем сказать:
Интерфейсы с единственной реализацией в рабочем коде — это код с запашком (code smell), потому что они могут быть плохими абстракциями из-за слишком тесной связи с их единственной реализацией.
При этом запахи кода не обязательно говорят о плохом или неправильном коде, просто их стоит изучить немного глубже, потому что они могут быть симптомом проблемы в дизайне ПО. Возможно, интерфейс был создан преждевременно, либо вручную, либо с помощью скаффолдинга, и тесно связан с деталями реализации единственного класса, в настоящее время выполняющего эту работу в системе. Знание того, что существует отношение 1:1 между интерфейсом и его реализацией, может помочь вам изучить дизайн и увидеть, есть ли с ним какие-либо проблемы (например, описанные выше, связанные с SOLID, стабильностью и т. д.). В этом смысле это может быть полезной эвристикой при оценке кода, но в идеале стоит оставить в системе все интерфейсы, которые имеют только одну реализацию, но которые на самом деле являются хорошими абстракциями.
Итого
Существует несколько определений абстракций. Некоторые аспекты могут оцениваться как истинные/ложные, например, является ли тип в C# абстрактным или конкретным. Другим может потребоваться некоторая субъективная оценка, чтобы определить, является ли данная абстракция хорошей, подлинной, полезной и т. д. А некоторые правила, претендующие на оценку того, является ли что-то абстракцией или нет, на самом деле просто говорят о том, является ли эта вещь хорошей абстракцией (или нет).
Источник: https://ardalis.com/what-are-abstractions-in-software-development/
Автор оригинала: Steve “Ardalis” Smith
👍10
День 1478. #ЗаметкиНаПолях #DesignPatterns
Паттерн Сервис/Репозиторий
Идея паттерна заключается в том, что код верхнего уровня должен полностью игнорировать бизнес-уровень и уровень данных приложения. То есть, если вы запрашиваете заказ, ваш код должен выглядеть примерно так:
Посмотрим на метод репозитория:
Что даёт этот паттерн? Типичный ответ: вы «можете захотеть сменить СУБД». На самом деле, это крайне маловероятно. Гораздо более вероятно, что вы захотите повторно использовать репозиторий в другом месте (возможно, из другого сервиса).
Особенности использования
1. Двойной репозиторий
Чаще всего это случается при использовании Entity Framework, который сам по себе является репозиторием. Использовать EF внутри репозитория обычно бесполезно, но бывают случаи, когда это имеет смысл. Например, если вы решили переключиться с EF на Dapper, нужно взять контекст БД, который использует сервис, и заменить его репозиторием.
2. Сквозной сервис
Рассмотрим следующий код метода сервиса:
3. Утечка ответственности
Это ситуация, когда один уровень начинает брать на себя ответственность от следующего уровня. Причина в том, что вы добавляете сложность, разделяя код на слои, но тесно связываете их, так что репозиторий зависит от сервиса. Например:
Более очевидный пример:
4. Внешний доступ
Представим, что мы вызываем внешний API. Конечно, этот вызов следует рассматривать так же, как вызов базы данных (в обоих случаях используются внешние для приложения зависимости). Почему бы не создать репозиторий, который бы вызывал API и обрабатывал его ответы так же, как если бы данные извлекались из базы? Таким образом, остальная часть системы остаётся полностью независимой от источника данных, и вы сможете поменять его в любое время.
Источник: https://pmichaels.net/service-repository-pattern/
Паттерн Сервис/Репозиторий
Идея паттерна заключается в том, что код верхнего уровня должен полностью игнорировать бизнес-уровень и уровень данных приложения. То есть, если вы запрашиваете заказ, ваш код должен выглядеть примерно так:
var order = orderService.GetOrder(12);Что делает GetOrder, не важно. Это ответственность сервиса. Хотя, там может быть что-то такое (здесь и далее псевдокод):
GetSalesOrder(id) {Сервис предоставляет некоторый функционал: получает заказ из репозитория, а затем, например, отправляет уведомление.
var order = repo.GetOrder(id);
if (order.HasAlert())
notifyService.SendAlert(order.Id);
return order;
}
Посмотрим на метод репозитория:
GetSalesOrder(id) {Репозиторий выбирает данные из двух таблиц БД и возвращает результат. Это пример хорошего использования паттерна сервис/репозиторий: каждая часть стека выполняет свою функцию.
var order = DB.RunSql(
"SELECT * FROM orders WHERE id = @id",
id);
var customer = DB.RunSql(
"SELECT * FROM customers WHERE id = @CustomerId",
order.CustomerId);
return new Order(order, customer);
}
Что даёт этот паттерн? Типичный ответ: вы «можете захотеть сменить СУБД». На самом деле, это крайне маловероятно. Гораздо более вероятно, что вы захотите повторно использовать репозиторий в другом месте (возможно, из другого сервиса).
Особенности использования
1. Двойной репозиторий
Чаще всего это случается при использовании Entity Framework, который сам по себе является репозиторием. Использовать EF внутри репозитория обычно бесполезно, но бывают случаи, когда это имеет смысл. Например, если вы решили переключиться с EF на Dapper, нужно взять контекст БД, который использует сервис, и заменить его репозиторием.
2. Сквозной сервис
Рассмотрим следующий код метода сервиса:
GetSalesOrder(id) {В этом случае он абсолютно ничего не делает, просто предоставляет оболочку, которую вы можете использовать. Можно сказать, что так весь код будет согласованным (контроллер > сервис > репозиторий), но это создаёт кучу лишних классов и делает код менее понятным. Очевидно, идеальная ситуация, когда сервис предоставляет дополнительную бизнес-логику.
return repo.GetOrder(id);
}
3. Утечка ответственности
Это ситуация, когда один уровень начинает брать на себя ответственность от следующего уровня. Причина в том, что вы добавляете сложность, разделяя код на слои, но тесно связываете их, так что репозиторий зависит от сервиса. Например:
GetSalesOrder(id) {Это кажется безобидным: на основе полученных данных мы должны или не должны получать дополнительные данные. Ясно, что какие данные извлекаются и как, - это ответственность репозитория, а не сервиса. Если мы рассматриваем примечания как особенность заказа, то везде, где извлекается заказ, потребуется эта логика. Т.е. вы не сможете вызвать репозиторий без сервиса. Если сервис и репозиторий связаны, зачем поддерживать два уровня абстракции?
var order = repo.GetOrder(id);
if (order.Name.StartsWith("a"))
order.Notes = repo.GetNotes(id);
…
return order;
}
Более очевидный пример:
CreateOrder(order, orderLines) {Очевидно, что транзакция принадлежит репозиторию и не имеет никакого отношения к сервису.
repo.StartTransaction();
repo.CreateOrder(order);
repo.CreateOrderLines(orderLines);
repo.CommitTransaction();
}
4. Внешний доступ
Представим, что мы вызываем внешний API. Конечно, этот вызов следует рассматривать так же, как вызов базы данных (в обоих случаях используются внешние для приложения зависимости). Почему бы не создать репозиторий, который бы вызывал API и обрабатывал его ответы так же, как если бы данные извлекались из базы? Таким образом, остальная часть системы остаётся полностью независимой от источника данных, и вы сможете поменять его в любое время.
Источник: https://pmichaels.net/service-repository-pattern/
👍9
День 1502. #DesignPatterns
Паттерн Скромный Объект
Скромный Объект (Humble Object) — это паттерн проектирования, специально предназначенный для упрощения модульного тестирования путём отделения поведения, которое легко обработать (логика предметной области), от поведения, которое трудно обработать (например, внешние события или зависимости). Рассмотрим, что это такое и как его можно использовать.
Программам часто необходимо взаимодействовать с другими частями компьютера или внешним миром, например с базой данных или интернет-сервисом. Но когда мы хотим протестировать наши программы, чтобы убедиться, что они работают правильно, может быть трудно общаться с этими другими частями.
Чтобы решить эту проблему, мы можем использовать паттерн Humble Object, отделив код программы, который должен взаимодействовать с другими частями компьютера или внешним миром, от кода программы, который этого не делает. Код, которому не нужно общаться с внешним миром, гораздо легче тестировать, потому что мы можем тестировать его изолированно, не беспокоясь о других вещах.
Простыми словами: нужно выделить сложные для тестирования части и поместить их в оболочку, вместо которой в тестах вы можете использовать заглушку или мок. Эта оболочка называется Humble object.
У вас когда-нибудь была зависимость от DateTime.Now? Если да, может быть очень сложно иметь стабильные тесты в зависимости от того, что вы делаете с Now. Тесты могут работать в будние дни, но проваливаться в выходные, если вы проверяете это в коде. Поэтому мы можем просто «извлечь» эту часть и поместить её в оболочку:
В то же время легко тестируемые вещи, как MyService, теперь получают зависимость, которая находится под вашим контролем. В тестах её можно заменить пустышкой или моком.
Итого
Паттерн Humble object может облегчить вашу жизнь, особенно на границах вашей архитектуры. Мы рассмотрели его на примере системного времени, но вы также можете применять это правило для таких вещей, как доступ к вводу-выводу или сети.
Источник: https://steven-giesel.com/blogPost/47acad0a-255c-489b-a805-d0f46bde23e5
Паттерн Скромный Объект
Скромный Объект (Humble Object) — это паттерн проектирования, специально предназначенный для упрощения модульного тестирования путём отделения поведения, которое легко обработать (логика предметной области), от поведения, которое трудно обработать (например, внешние события или зависимости). Рассмотрим, что это такое и как его можно использовать.
Программам часто необходимо взаимодействовать с другими частями компьютера или внешним миром, например с базой данных или интернет-сервисом. Но когда мы хотим протестировать наши программы, чтобы убедиться, что они работают правильно, может быть трудно общаться с этими другими частями.
Чтобы решить эту проблему, мы можем использовать паттерн Humble Object, отделив код программы, который должен взаимодействовать с другими частями компьютера или внешним миром, от кода программы, который этого не делает. Код, которому не нужно общаться с внешним миром, гораздо легче тестировать, потому что мы можем тестировать его изолированно, не беспокоясь о других вещах.
Простыми словами: нужно выделить сложные для тестирования части и поместить их в оболочку, вместо которой в тестах вы можете использовать заглушку или мок. Эта оболочка называется Humble object.
У вас когда-нибудь была зависимость от DateTime.Now? Если да, может быть очень сложно иметь стабильные тесты в зависимости от того, что вы делаете с Now. Тесты могут работать в будние дни, но проваливаться в выходные, если вы проверяете это в коде. Поэтому мы можем просто «извлечь» эту часть и поместить её в оболочку:
public interface IDateTimeProviderМы отделили трудно тестируемые вещи от легко тестируемых. Трудно тестируемые вещи, в идеале, вообще не нуждаются в тестировании. Тестировать DateTime.Now вообще не лучшая идея. Во-первых, как бы вы реализовали такой тест? А во-вторых, это не ваш код. С вашей точки зрения, DateTime.Now — это сторонний код, и не ваша задача его тестировать, просто допустим, что он работает правильно.
{
DateTime Now { get; }
}
public class DateTimeProvider : IDateTimeProvider
{
public DateTime Now => DateTime.Now;
}
public class MyService
{
public MyService(
IDateTimeProvider dateTimeProvider)
{
…
}
}
В то же время легко тестируемые вещи, как MyService, теперь получают зависимость, которая находится под вашим контролем. В тестах её можно заменить пустышкой или моком.
Итого
Паттерн Humble object может облегчить вашу жизнь, особенно на границах вашей архитектуры. Мы рассмотрели его на примере системного времени, но вы также можете применять это правило для таких вещей, как доступ к вводу-выводу или сети.
Источник: https://steven-giesel.com/blogPost/47acad0a-255c-489b-a805-d0f46bde23e5
👍16👎1
День 1543. #Курсы
Сегодня порекомендую вам ютуб канал Зонара Хорвата (Zoran Horvat) Зоран - консультант, разработчик и архитектор ПО, автор на Pluralsight, Udemy и YouTube. На его канале вы найдёте советы по разработке и архитектуре, паттернах проектирования, чистом коде, новинкам языка C# и т.п.
Видео, которое попалось мне, вышло совсем недавно и называется Are Design Patterns Dead in C#? (Паттерны Проектирования в C# Умерли?) В нём Зоран на примере оригинальной книги «Паттерны Проектирования» банды четырёх рассуждает, полезна ли до сих пор информация о паттернах проектирования и применимы ли они в современной разработке в .NET.
Не буду спойлерить, смотрите до конца)))
Кстати, про паттерны проектирования вы можете почитать на канале по тегу #DesignPatterns.
Сегодня порекомендую вам ютуб канал Зонара Хорвата (Zoran Horvat) Зоран - консультант, разработчик и архитектор ПО, автор на Pluralsight, Udemy и YouTube. На его канале вы найдёте советы по разработке и архитектуре, паттернах проектирования, чистом коде, новинкам языка C# и т.п.
Видео, которое попалось мне, вышло совсем недавно и называется Are Design Patterns Dead in C#? (Паттерны Проектирования в C# Умерли?) В нём Зоран на примере оригинальной книги «Паттерны Проектирования» банды четырёх рассуждает, полезна ли до сих пор информация о паттернах проектирования и применимы ли они в современной разработке в .NET.
Не буду спойлерить, смотрите до конца)))
Кстати, про паттерны проектирования вы можете почитать на канале по тегу #DesignPatterns.
YouTube
Are Design Patterns Dead in C#?
Become a patron and gain access to source code and exclusive live streams: https://www.patreon.com/posts/are-design-dead-81382714
When was the last time you implemented a proper design pattern in your object-oriented code? Not a factory method, but a proper…
When was the last time you implemented a proper design pattern in your object-oriented code? Not a factory method, but a proper…
👍17
День 1697. #DesignPatterns
Принцип Применения Принципов: Когда НЕ Использовать SOLID
Мы знаем много примеров того, когда и как использовать принципы SOLID. Есть ли какие-нибудь хорошие примеры того, когда соблюдение этих принципов является плохой идеей?
Принцип Применения Принципов (Principle of Applying Principles - POAP):
Принципы, модели и лучшие практики не являются конечными целями. Хорошее и правильное применение каждого из них вдохновляется и ограничивается высшей, более конечной целью. Нужно понимать, почему вы делаете то, что делаете.
Т.е. позиция по умолчанию - не применять принцип, пока вы достаточно хорошо не поймёте, почему вы его применяете*. Слепая вера во что-либо имеет тенденцию приводить к проблемам, и принципы разработки ПО не являются исключением. Обычно это просто потребует дополнительной работы и может привести к результатам, противоположным самой сути принципа.
*Это обычно предполагает, что у вас нет техлида, на которого можно положиться. Если вы младший инженер или новичок в команде, фраза «старший коллега сказал мне, применить этот принцип, и я ему доверяю» — это достаточная (хотя и временная) причина просто применить принцип. Пока вы не вырастете в своей роли и не возьмёте на себя больше ответственности.
Создайте шпаргалку, чего должен достигать каждый принцип, к каким проблемам он может привести, и добавьте примечания для вашего домена.
1. Принцип единственной обязанности (SRP)
Цель: сделать изменения изолированными, их легко тестировать и анализировать.
Проблемы: гипердекомпозиция. Вряд ли вам потребуется отдельный класс для установки заголовка страницы.
Примечания по конкретному домену: это ваше домашнее задание.
2. Принцип «открыт/закрыт» (OCP)
Цель: предотвратить выход из строя старых функций при реализации новых.
Проблемы: «закрытие» модуля, которым вы владеете, поддерживаете и можете безопасно расширять, может привести к ненужной сложности, цепочкам наследования и увеличению размера кода.
Примечания: …
3. Принцип подстановки Лисков (LSP)
Цель: предотвратить появление неправильного кода, который выглядит правильно.
Проблемы: чрезмерная уверенность в LSP как в сигнале правильности.
Примечания: …
4. Принцип разделения интерфейса (ISP)
Цель: уменьшить связанность между классами и их клиентами.
Проблемы: требование минимально возможного интерфейса может противоречить намерениям LSP.
Примечания: …
5. Принцип инверсии зависимостей (DIP)
Цель: обеспечить возможность повторного использования, замены и тестирования большей части системы.
Проблемы: чрезмерная инверсия. Создание и «инвертирование» абстракций без всякой выгоды, усложнение чтения и понимания кода.
Примечания: …
Бонус. Не повторяйтесь (DRY).
Цель: предотвратить несогласованное применение бизнес-правил в системе.
Проблемы: чрезмерное применение. Код, который выглядит одинаково, может служить разным целям, а поведение, которое должно отличаться, становится трудно изменить.
Примечания: …
Заметки выше краткие и общие, и вы можете не соглашаться с целями и проблемами. Хорошо изучите и поразмышляйте над каждым принципом и над тем, как он применим к вашей конкретной системе и домену. Применимость на самом деле зависит от вещей, которые знает только эксперт в предметной области: что может измениться? какая логика будет сквозной? и т. д. Думайте, делайте заметки и делитесь ими со своей командой.
Помните, что даже POAP подлежит POAP! И цель POAP — удержать вас от совершения глупых, расточительных или вредных поступков ради принципов. Спор из-за принципов обычно является нарушением как намерения POAP, так и, возможно, смысла принципа, о котором вы спорите.
Источник: https://softwareengineering.stackexchange.com/questions/447532/when-to-not-use-solid-principles
Принцип Применения Принципов: Когда НЕ Использовать SOLID
Мы знаем много примеров того, когда и как использовать принципы SOLID. Есть ли какие-нибудь хорошие примеры того, когда соблюдение этих принципов является плохой идеей?
Принцип Применения Принципов (Principle of Applying Principles - POAP):
Принципы, модели и лучшие практики не являются конечными целями. Хорошее и правильное применение каждого из них вдохновляется и ограничивается высшей, более конечной целью. Нужно понимать, почему вы делаете то, что делаете.
Т.е. позиция по умолчанию - не применять принцип, пока вы достаточно хорошо не поймёте, почему вы его применяете*. Слепая вера во что-либо имеет тенденцию приводить к проблемам, и принципы разработки ПО не являются исключением. Обычно это просто потребует дополнительной работы и может привести к результатам, противоположным самой сути принципа.
*Это обычно предполагает, что у вас нет техлида, на которого можно положиться. Если вы младший инженер или новичок в команде, фраза «старший коллега сказал мне, применить этот принцип, и я ему доверяю» — это достаточная (хотя и временная) причина просто применить принцип. Пока вы не вырастете в своей роли и не возьмёте на себя больше ответственности.
Создайте шпаргалку, чего должен достигать каждый принцип, к каким проблемам он может привести, и добавьте примечания для вашего домена.
1. Принцип единственной обязанности (SRP)
Цель: сделать изменения изолированными, их легко тестировать и анализировать.
Проблемы: гипердекомпозиция. Вряд ли вам потребуется отдельный класс для установки заголовка страницы.
Примечания по конкретному домену: это ваше домашнее задание.
2. Принцип «открыт/закрыт» (OCP)
Цель: предотвратить выход из строя старых функций при реализации новых.
Проблемы: «закрытие» модуля, которым вы владеете, поддерживаете и можете безопасно расширять, может привести к ненужной сложности, цепочкам наследования и увеличению размера кода.
Примечания: …
3. Принцип подстановки Лисков (LSP)
Цель: предотвратить появление неправильного кода, который выглядит правильно.
Проблемы: чрезмерная уверенность в LSP как в сигнале правильности.
Примечания: …
4. Принцип разделения интерфейса (ISP)
Цель: уменьшить связанность между классами и их клиентами.
Проблемы: требование минимально возможного интерфейса может противоречить намерениям LSP.
Примечания: …
5. Принцип инверсии зависимостей (DIP)
Цель: обеспечить возможность повторного использования, замены и тестирования большей части системы.
Проблемы: чрезмерная инверсия. Создание и «инвертирование» абстракций без всякой выгоды, усложнение чтения и понимания кода.
Примечания: …
Бонус. Не повторяйтесь (DRY).
Цель: предотвратить несогласованное применение бизнес-правил в системе.
Проблемы: чрезмерное применение. Код, который выглядит одинаково, может служить разным целям, а поведение, которое должно отличаться, становится трудно изменить.
Примечания: …
Заметки выше краткие и общие, и вы можете не соглашаться с целями и проблемами. Хорошо изучите и поразмышляйте над каждым принципом и над тем, как он применим к вашей конкретной системе и домену. Применимость на самом деле зависит от вещей, которые знает только эксперт в предметной области: что может измениться? какая логика будет сквозной? и т. д. Думайте, делайте заметки и делитесь ими со своей командой.
Помните, что даже POAP подлежит POAP! И цель POAP — удержать вас от совершения глупых, расточительных или вредных поступков ради принципов. Спор из-за принципов обычно является нарушением как намерения POAP, так и, возможно, смысла принципа, о котором вы спорите.
Источник: https://softwareengineering.stackexchange.com/questions/447532/when-to-not-use-solid-principles
👍19👎1