Что такое программный компонент
Эта и несколько последующих глав пытаются дать понимание, почему вопрос: «Где правильно расположить границы между компонентами?» в некоторых случаях может быть некорректен. Нередко программист, задавая его, предполагает, что границы компонентов определяются Bounded Context’ами. Однако Bounded Context является лишь одним из факторов, и нередко его можно вовсе не учитывать.
Компонент — это часть системы, которая решает подзадачу такого объёма, который разработчик может полностью понять и реализовать за разумное время и независимо от других разработчиков. Компонент — это чёрный ящик с собственным жизненным циклом.
Приведённое определение может звучать несколько необычно, однако единственной значимой причиной существования компонентов является неспособность человека решать огромную задачу целиком. Главным подходом к снижению когнитивной нагрузки является разбивка такой задачи на подзадачи. Решение каждой подзадачи оформляется в виде изолированного блока, который прячет всю сложность внутри, оставляя снаружи упрощённый механизм для взаимодействия с другими подобными блоками. Другими словами, решением сложной задачи является набор «чёрных ящиков».
Выбор границ подзадач не ограничен строгими правилами, и разработчик волен проводить их по своему усмотрению, опираясь на архитектурные решения и технические потребности системы. Например, если предполагается большое количество запросов на чтение, задачу можно разделить на подзадачи чтения и записи. В этом случае компонент чтения приобретает возможность быть горизонтально масштабируемым, тогда как компонент записи остаётся единственным в системе. Часто границы компонента соответствуют границам Bounded Context’а, однако система может состоять и из набора более мелких компонентов. Крайним случаем такой системы будет такая, которая содержит компоненты, состоящие всего-лишь из одного агрегата (иногда такая архитектура называется наносервисной).
«Чёрный ящик» без жизненного цикла обычно является библиотекой. Если же «чёрный ящик» обладает собственным жизненным циклом, он становится компонентом. Под жизненным циклом здесь понимается наличие точки запуска, которая создаёт (инстанцирует) публично доступный синглтон-объект, методы которого можно вызывать в любой момент времени. Под собственным жизненным циклом подразумевается то, что каждый «чёрный ящик» может быть запущен независимо от других. Например, приложение обычно продолжает работать даже тогда, когда база данных на некоторое время становится недоступной. Это возможно потому, что база данных, сама являясь компонентом, обладает собственным жизненным циклом. Здесь следует подчеркнуть разницу между компиляционными зависимостями и зависимостями компонентов друг от друга. Отсутствие компиляционной зависимости делает код некомпилируемым. Однако при отсутствии зависимых компонентов текущий компонент продолжает работать, хотя часть запросов будет завершаться с ошибками. Например, если отключить базу данных, приложение, хотя и выдаёт ошибки при запросах, не перестаёт работать.