Backend Typescript 1.0.0 Help

Компонентно-ориентированная модель

Что такое КОП и зачем она нужна

Компонентно-ориентированное программирование (КОП) — это подход, где базовой единицей проектирования является не класс и не функция, а компонент — автономный, повторно используемый модуль с чётко определёнными контрактами: какими интерфейсами он обладает (предоставляет) и какие интерфейсы требует от окружения.

  • Компонент — это единица поставки и замены (модуль или артефакт: .jar, npm-пакет, Go-модуль, .so/.dll).

  • Граница компонента — это контракт (интерфейсы, схемы данных, протоколы, инварианты).

  • Компоненты соединяются через порты и адаптеры, а не через внутренние детали.

  • КОП поощряет слабую связность, сильную сочетаемость и контролируемую эволюцию версий.

Компонент vs класс, модуль и сервис

Класс — строительный блок кода. Модуль (package) — блок компоновки. Компонент — блок поставки и замены. Он может содержать несколько модулей и классов, иметь свой цикл жизни и версионирование. Сервис — это запущенный процесс/экземпляр, который может быть реализован одним или несколькими компонентами.

Контракты компонентов: предоставляемые и требуемые интерфейсы

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

Мини-пример: компонент приветствия использует часы (требуемый порт)

Компонент Greeter предоставляет метод greet(), а требует порт Clock. Связь — через внедрение зависимостей.

#include <iostream> #include <string> struct IClock { virtual ~IClock() = default; virtual std::string Now() const = 0; }; struct SystemClock : IClock { std::string Now() const override { return "2025-09-24T21:00:00Z"; } }; class Greeter { const IClock& clock; public: explicit Greeter(const IClock& c) : clock(c) {} void Greet() const { std::cout << "Hello! Now: " << clock.Now() << std::endl; // Hello! Now: 2025-09-24T21:00:00Z } }; int main() { SystemClock c; Greeter g(c); g.Greet(); }
interface Clock { String now(); } class SystemClock implements Clock { public String now() { return "2025-09-24T21:00:00Z"; } } class Greeter { private final Clock clock; Greeter(Clock clock) { this.clock = clock; } void greet() { System.out.println("Hello! Now: " + clock.now()); // Hello! Now: 2025-09-24T21:00:00Z } } public class Main { public static void main(String\[] args) { Clock c = new SystemClock(); new Greeter(c).greet(); } }
class Greeter { constructor(clock) { this.clock = clock; } greet() { console.log("Hello! Now: " + this.clock.now()); // Hello! Now: 2025-09-24T21:00:00Z } } const clock = {now: () => "2025-09-24T21:00:00Z"}; new Greeter(clock).greet();
interface Clock { now(): string; } class SystemClock implements Clock { now(): string { return "2025-09-24T21:00:00Z"; } } class Greeter { constructor(private clock: Clock) { } greet(): void { console.log("Hello! Now: " + this.clock.now()); // Hello! Now: 2025-09-24T21:00:00Z } } new Greeter(new SystemClock()).greet();
package main import "fmt" type Clock interface{ Now() string } type SystemClock struct{} func (SystemClock) Now() string { return "2025-09-24T21:00:00Z" } type Greeter struct{ clock Clock } func (g Greeter) Greet() { fmt.Println("Hello! Now: " + g.clock.Now()) // Hello! Now: 2025-09-24T21:00:00Z } func main() { g := Greeter{clock: SystemClock{}} g.Greet() }

Композиция и внедрение зависимостей (DI)

Композиция — это сборка приложения из компонентов. DI автоматизирует связывание требуемых портов с адаптерами/реализациями. Допустима ручная композиция, контейнер DI, или плагин-механизм (discovery & loading).

Жизненный цикл компонента

Обычно включает этапы: инициализация (чтение конфигурации, проверка окружения), старт (подключение ресурсов), работа, останов (безопасное завершение, освобождение ресурсов).

  • Методы вроде init()/start()/stop()/dispose().

  • Идентичность и состояние компонента должны быть управляемыми и наблюдаемыми (health-check).

  • Кросс-срезы (логирование, метрики, трассировка) подключайте декораторами/обёртками, не загрязняя доменную логику.

Версионирование, совместимость и эволюция

SemVer помогает согласовать ожидания: MAJOR — несовместимые изменения контракта; MINOR — добавления, сохраняющие совместимость; PATCH — исправления. Для бинарных библиотек учитывайте ABI, для исходных — API-совместимость.

  • Избегайте ломающих изменений в предоставляемых интерфейсах без MAJOR-bump.

  • Добавляйте по умолчанию необязательные параметры/флаги, а старые помечайте deprecated с переходным периодом.

  • Вводите адаптеры между версиями, если нужно поддержать старых клиентов.

Коммуникации: внутри процесса и между процессами

Связи бывают внутрипроцессные (вызов метода/функции) и межпроцессные (RPC, REST/gRPC, сообщения в шине). Чем «дальше» граница, тем грубее должен быть интерфейс (меньше чата), стабильнее схема данных и чётче обработка ошибок.

Тестирование компонентов

  • Юнит-тесты — проверяют внутреннюю логику.

  • Контрактные тесты — фиксируют поведение предоставляемых интерфейсов и ожидания требуемых.

  • Интеграционные тесты — проверяют связки компонентов с реальными адаптерами.

Наблюдаемость и эксплуатация

Компонент должен экспортировать метрики (счётчики ошибок, латентность), логи с контекстом, трассировки на границах и health-checks. Это ускоряет диагностику и повышает предсказуемость релизов.

Антипаттерны и подводные камни

  • God-компонент: «компонент-всё». Тяжело версионировать и тестировать.

  • Циклические зависимости: затрудняют сборку и эволюцию контрактов.

  • Протекающая абстракция: наружу выползают детали хранения/передачи данных.

  • Скрытые зависимости: использование глобальных синглтонов/локаторов.

  • Чатливые интерфейсы: множество мелких вызовов через границу.

Практический чек-лист проектирования компонента

  • Определите границу и контракты (предоставляемые/требуемые).

  • Спроектируйте порты и адаптеры (хранилище, сеть, очередь, UI).

  • Выберите стратегию композиции (ручная, DI, плагины).

  • Опишите жизненный цикл и ресурсы (конфигурация, пулы, тайм-ауты).

  • Зафиксируйте политику версионирования и депрекейта.

  • Добавьте наблюдаемость: логи, метрики, трассировки, health.

  • Подготовьте тесты: юнит, контрактные, интеграционные.

  • Оцените гранулярность: не слишком крупный и не слишком мелкий.

Примеры упаковки и поставки (по экосистемам)

  • C++: статические/динамические библиотеки, pkg-config, контроль ABI.

  • Java: артефакты Maven/Gradle (.jar/.aar), манифесты, OSGi/JPMS.

  • JavaScript/TypeScript: npm-пакеты, export-карта, типы .d.ts.

  • Go: модули (go.mod), семантика совместимости пакетов.

Last modified: 01 October 2025