Как стать автором
Обновить
4
0

Systems Architect

Отправить сообщение
Очевидно же..
Википедия — источник, конечно, так себе… ну да ладно, в каком именно предложении вам очевидно?

Автору термина «OOP», Alan Kay, например, это не очевидно (или, даже, очевидно не это): «OOP to me means only messaging, local retention and protection and hiding of state-process, and extreme late-binding of all things.»
ООП говорит нам — лучше да, чем нет, но необязательно.
Можно поинтересоваться, где он так говорит?
Я уже не раз пытался понять «что же такое ООП» и не смог найти того кирпичика который является его основой.
Он раскрывает эту тему достаточно доходчиво и с обзором истории в «Clean Architecture: A Craftsman’s Guide to Software Structure and Design», 2017
Я имел ввиду, что остальные технические знания абстрактны от конкретного языка. Это вопросы проектирования, архитектуры, алгоритмы, базы данных, тестирование, рефакторинг, качество кода, методологии разработки, распределенные системы, операционная система и общее устройство компиляторов/интерпретаторов, методики оценивания задач и т.п.

Знание того, что под капотом, требуется только общее, чтобы понимать алгоритмическую сложность тех или иных конструкций. Есть разница между «программированием на языке» и «программированием с использованием языка».
Звучит хорошо, но я никогда такого не видел.
Обе цитаты из «Clean Architecture: A Craftsman’s Guide to Software Structure and Design», 2017

В базах данных очень много нюансов, и баланс выбора постоянно меняется в том числе по мере неравномерного развития самих БД. Причем, для каждого Bounded Context существует свой собственный баланс. Не хочу вдаваться в технические подробности — это обширная тема.

простейшее разделение на commands/queries уже очень помогает.
Да, и я даже приводил в качестве примера ссылку на одно из лучших демонстрационных эталонных приложений (reference application). Это хорошо работает, когда логика простая, например, запрос возвращает сразу DTO. Для более сложных случаев, когда используются усложненные сценарии создания агрегата, используется Identity Map и т.п., такой подход может привести к понижению Cohesion, и росту таких Code Smells как Shotgun Surgery или Divergent Change, что негативно отражается на экономике разработки. Хороший код всегда стремится к Low Coupling & High Cohesion. До тех пор, пока Вы в этом балансе — все ок.
Ну, не знаю, я, например, языки меняю как перчатки. Многие популярные языки семантически сильно похожи. У хорошего разработчика знание синтаксиса конкретного языка занимает не более 5-ти процентов. Так что, как говорится, «глаза боятся — руки делают». Дерзайте. Попробуйте найти удаленную работу, если в городе небольшой выбор. Или рассмотрите вариант переезда. Серьезная математическая подготовка — это серьезный аргумент, чтобы рассмотреть переезд в Москву.
У меня товарищ тоже преподавал математику в ВУЗе. Потом стал программистом, попал в крупную компанию. Там на него обратил внимание отдел Data-solutions, и его математические навыки очень даже пригодились. Есть в программировании области, где математика важна. Двигайтесь в сторону Machine learning.
Если вы (корректно) используете реляционную базу, значит, она подходит под вашу модель данных и «просто» переехать, особенно целиком, на другое хранилище не просто сложно, а зачастую абсолютно не нужно.

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

Например, при распиле монолита, часто применяются принципы DDD, и агрегато-ориентированные базы данных становятся более привлекательными. Часто вопрос пересмотра выбора БД наступает в момент назревания необходимости шардить данные. Иногда БД перестает удовлетворять при добавлении каких-то новых бизнес-фич. Или, например, требуется внедрить Event Sourcing. Иногда приходится рассматривать проприетарные cloud-решения, предлагаемые крупными хостинг-провайдерами.

Именно поэтому, раз уж мы говорим о Clean Architecture, Роберт Мартин говорит:

«It is not necessary to choose a database system in the early days of development, because the high-level policy should not care which kind of database will be used. Indeed, if the architect is careful, the high-level policy will not care if the database is relational, distributed, hierarchical, or just plain flat files.»

«From an architectural point of view, the database is a non-entity—it is a detail that does not rise to the level of an architectural element. Its relationship to the architecture of a software system is rather like the relationship of a doorknob to the architecture of your home.»

Хороший архитектор максимизирует количество непринятых решений.
Хорошо, вместо контроллера теперь есть набор «функций» для доступа к модели, которые, кстати, тоже можно структурировать, используя каталоги файловой системы, например.

Это пока еще не функции, а паттерн Command, так же как и UseCase/Interactor являются разновидностью этого же паттерна. Технически можно сделать их чистыми функциями, здесь Greg Young рассказывает как это сделать.

С точки зрения Crean Architecture, Front/Page Conttroller — это всего лишь IO-устройство. А с точки зрения Hexagonal Architecture, это всего лишь один из портов. Поэтому, для Clean Architecture вопрос реализации роутинга и пр. не имеет принципиального значения.

У Вас чередуются термины Dependency Injection и Dependency Inversion, — это вещи немного разные, хотя последний и может реализовываться посредством первого.
При этом не совсем понятно, что вы имеете ввиду под DI, обычно, чтобы их отличать, Dependency Inversion пишется полностью — Dependency Inversion Principle (DIP).

сторонник практики инверсии зависимостей (Dependency Inversion, D в SOLID). Поэтому подобные зависимости мне нужно инициализировать где-то снаружи и передавать в конструктор контроллера.

DIP — это политика. Во втором предложении цитаты вы говорите уже о механизме пассивного внедрения зависимостей. Кстати, не обязательно в конструктор, можно и через сеттеры. В случае с конструктором можно использовать каррирование для управления зависимостями (не уверен что в Ruby это возможно).

# Плохо (инициализируем зависимость в конструкторе)

Вы, наверное, имели ввиду Service Locator или Plugin Pattern.

Но мы пойдём дальше и реализуем паттерн Unit of Work

В популярных ОРМ, объект Session реализует не Unit of Work, а паттерн Facade, чтобы облегчить работу с множеством компонентов ORM. Unit of Work не должен содержать метод .load(), так как это уже обязанность Repository/DataMapper.

Здесь возникает ощущение дежавю, как и с контроллерами: а не является ли репозиторий такой же рудиментарной сущностью? Забегая наперёд, отвечу — да, является.

Здесь вы немного поспешили с выводами. А что если Вашему проекту потребуется заменить RDBMS на Polyglot persistence (например, NoSQL + Graph Database) или даже ApplicationDatabase? Хороший архитектор максимизирует количество непринятых решений, и именно для решения этой задачи паттерн Repository и предназначен. Часто он, для достижения полного сокрытия источника данных, используется совместно с паттерном QueryObject или Specification.
Ну и еще такой вопрос — при использовании Polyglot Persistence, как вы думаете осуществлять двухфазные/компенсационные транзакции в Вашем случае?

То есть, мы можем отказаться от Repository и Controller в пользу единого унифицированного Action!

Только не Action, а паттерн Command. Такой подход активно применяется в CQRS.
Только Repository создается обычно для обслуживания одной доменной модели, или даже — одного агрегата.
Попробуйте добавить поддержку, например, Identity Map, и вы очень скоро обнаружите, что ваш подход приводит к Low Cohesion.

def initialize(attrs, payment_service)

Если уж вы и решили присовокупить сервис уровня приложения к доменной модели, то лучше было бы это сделать через Cross-Cutting Concern, Robert Martin как раз об этом пишет в Clean Code. Но лучше было бы их не присовокуплять, а использовать Domain Event.

Поэтому в рамках фреймворка Dandy я реализовал роутер

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

P.S.: В целом, Вы на правильном пути. Видно желание докопаться до истины и результат. Надеюсь, что чем-то смог Вам помочь.
А извне их кто-то при этом может менять?
Если Вы правильно наложите ограничение — то не может. Можно даже наложить это ограничение технически, используя, например, обособленный сетевой периметр, ACL, DB-триггер и т.п. А лучше даже использовать специализированные хранилища. Хотя, чисто технически, можно подменить даже stack вызова функции, — так что тут все зависит от баланса стоимости затрат на реализацию этого ограничения и выгод от его взлома.
Смысл появляется тогда, когда у вас существуют функции, которые не живут в IO.

Ну почему же. С IO-устройствами тоже можно обращаться функционально, если наложить ограничение на изменяемость данных, т.е. если от CRUD отбросить Update и Delete. Именно в этом и заключается идея Event Sourcing, со слов автора термина CQRS Greg Young. Там у него есть еще вот такой интересный доклад на тему функциональной обработки данных.
Таки да. В IDDD, правда, бегло отыскать внятно сформулированного ответа мне не удалось, а вот в DDD Distilled это сформулировано предельно ясно:
«When using DDD, a Bounded Context should align one-to-one (1:1) with a single Subdomain. That is, when using DDD, if there is one Bounded Context, there is, as a goal, one Subdomain model in that Bounded Context. It may not always be possible or practical to achieve, but where possible it is important to design in that way. This will keep your Bounded Contexts clean and focused on the core strategic initiative.»
Есть отличия. В основном этот термин используется для отделения смыслового ядра от неспециализированных подобластей (для которых можно применить готовые решения из коробки, например, учет прихода-расхода, и освободить ресурсы для более качественной проработки смыслового ядра). Глава 15 у Эванса. Просто пример у Chris Richardson по ссылке не самый удачный, сбивает с толку (что видно по комментариям внизу примера).
Что-то мне так кажется, что вы говорите о нарушении автономности сервисов, если я правильно Вас понял.
Я как раз имел ввиду случай, когда BC состоит из нескольких физических сервисов. Это относительно редкое исключение. В основном, один BC — один сервис.
максимально следовать принципам KISS и YAGNI и не усложнять пока это возможно.
Суть YAGNI заключается не в том, чтобы не усложнять, а в достижении наилучшей экономики разработки в условиях неполной информированности. А это значит, что выделение лишних абстракций (и любое другое усложнение) оправдано лишь в том случае, если стоимость их выделения в будущем будет существенно дороже, чем сейчас.

С другой стороны, «хороший архитектор всегда максимизирует количество непринятых решений», обеспечивая при этом высокие экономические показатели разработки при любом сценарии развития программы. YAGNI должен способствовать эволюции программы, а не препятствовать ей. Например, если код нарушает Open-Closed Principle (OCP) или Stable Dependencies Principle (SDP), то это, на самом деле, не YAGNI, поскольку это противоречит основной его цели — достижение наилучшей экономики разработки в условиях неполной информированности.

Приведенный вами фрагмент кода нарушает Stable Dependencies Principle (SDP). Оправдано ли это нарушение? Ответ зависит от баланса получаемых выгод и затрат. Если у Вас всего один такой метод — разумеется, вам дешевле пофиксить его, когда публичный интерфейс веб-фреймворка будет изменен. С другой стороны, мне известны проекты, которые на протяжении двух лет не могли соскочить со старой версии используемого фреймворка. Хорошее приложение стремится минимизировть зависимость от конкретного фреймворка и от конкретной его версии.

но пихать её вообще везде глупо.
Конечно.
Определенная доля правды в Вашем ответе, действительно, присутствует. Репутация Agile сегодня на рынке, действительно, пострадала. И пострадала она вполне предсказуемо, и здесь масло в огонь подлил Ken Schwaber, когда убедил Jeff Sutherland «оставить инженерные практики за рамками Scrum, чтобы упростить модель и позволить командам брать на себя ответственность за выбор тех или иных практик».

С одной стороны, это облегчило освоение Scrum широкими массами. Это стало модным и массовым. С другой стороны, то, что осваивали широкие массы, на самом деле было немного далековато от принципиальной идеи Agile. Итеративное планирование — это не первопричина, а технически-обоснованная бизнес-возможность, достигаемая в результате Agile-разработки, и без последнего она теряет экономический смысл по сравнению с BDUF. «A steep change cost curve makes XP (Agile) impossible» — Kent Beck (основатель XP, подписант Agile Manifesto, наставник Роберта Мартина).

А по поводу «информационный энтропии», то «хороший архитектор максимизирует количество непринятых решений» — Роберт Мартин. Именно поэтому, уже на следующий год после того, как он в 2001 году организовал собрание для подписания Agile Manifesto, он выпустил книгу «Agile Software Development. Principles, Patterns, and Practices.», которая была посвящена тому, как нужно писать код, чтобы было возможным работать по Agile.

Ключ к качеству — не следует избегать первоначального проектирования, тут без специалиста в этой области не обойтись.

Конечно. Именно об этом говорит Kent Beck: «McConnell writes, „In ten years the pendulum has swung from 'design everything' to 'design nothing.' But the alternative to BDUF [Big Design Up Front] isn’t no design up front, it’s a Little Design Up Front (LDUF) or Enough Design Up Front (ENUF).“ This is a strawman argument. The alternative to designing before implementing is designing after implementing. Some design up-front is necessary, but just enough to get the initial implementation. Further design takes place once the implementation is in place and the real constraints on the design are obvious. Far from 'design nothing,' the XP strategy is 'design always.'»

Ну, и, классическое разъяснение по этому поводу — Is Design Dead?

От себя добавлю, что лично я редко встречал успешный Scrum, и больше предпочитаю Extreme Programming (XP) для небольших команд и SAFe для крупных проектов. Даже если мы и вынуждены работать по Scrum в силу каких-то формальных причин, то всегда стараемся комбинировать его с XP.
Совершенно верно, часто, (за редким исключением — «Sometimes, a BC could be composed of several physical services, but not vice versa.», пример).
Для юнит-теста надо надо мокать базу, для этого надо пилить слои. Надо делать, какой-нибдуь IRepository.GetUserProfile(), делать слой данных, делать мэппинг данных из UserProfile в UserProfileResponse. (Вы же не будете класть объекты уровня Api в DAL).

Классический вопрос, озвученный в 2014 году David Heinemeier Hansson: "TDD is dead. Long live testing." (хотя на самом деле с TDD он мало связан, но, так случилось, что объектом его критики стал именно TDD, вероятно потому, что архитектура Ruby on Rails часто критиковалась приверженцами TDD). Результатом его поста стал 5-ти серийный сериал "Is TDD Dead?", в котором подробно раскрывается озвученная Вами проблема. В сериале принимал участие сам David Heinemeier Hansson, создатель Ruby on Rails, Martin Fowler, автор книги PoEAA, по которой и был создан Ruby on Rails, и Kent Beck — основатель TDD и друг Martin Fowler. Т.е. более компетентный ответ сложно найти.

Проблема данного кода не в том, что его трудно протестировать изолированно (изоляцию, на самом деле, легко осуществить на уровне коннектора БД, о чем писал Кент Бек в «Test-Driven Development By Example»), если изоляцию вообще нужно делать, а в том, что в этом коде невозможно отделить то, ЧТО он делает, от того, КАК он это делает. Т.е. смешаны политики разного уровня, которые будут изменяться в разное время, с разной частотой и по разным причинам. В качестве примера можно привести некогда популярный веб-фреймворк Pylons, который прекратил свое существование, крупные обратно-несовместимые изменения в веб-фреймворке CherryPy, которые отразились на другом веб-фреймворке TurboGears, и т.п. На фронтенде хорошо известна история с совершенно обратно-несовместимым релизом Angular2.

И здесь перед вами возникает вопрос поиска баланса выгод и затрат — что произойдет, если IO-устройство (а, с точки зрения принципов Clean Architecture, Front/Page Controller — это просто IO-устройство) выпустило обратно-несовместимый релиз или объявило о прекращении своего существования? Что произойдет, если вам понадобится добавить поддержку, например, CLI-интерфейса? Дешевле ли Вам заложить уровень абстракции сейчас, и защитить свою политику более высокого уровня от низкоуровневых изменений, или же вам дешевле каждый «апгрейдить» свой код? Каждое решение — это поиск компромиса.

В связи с тем, что в последнее время заметно усилился интерес к CQRS, построение запроса обычно инкапсулируется в Query объект (пример), при этом не обязательно использовать ни доменные модели (по причине вырожденности их поведения), ни Repository. Сторонники Clean Architecture выделяют класс UseCase/Interactor, который, так же, как и класс Query, является разновидностью паттерна Команда, но, по определению, имеет немного более высокий уровень политики.

Вы можете подсказать название статического анализатора, который умеет полноценно распознавать классифицированные Code Smells из описанных каталогов? К примеру, распознавать Shotgun Surgery или Divergent Change? С удовольствием выслушал бы. Известные мне анализаторы умеют распознавать в лучшем случае лишь Feature Envy (не считая проверки Style Guide).

Информация

В рейтинге
Не участвует
Откуда
Москва, Москва и Московская обл., Россия
Зарегистрирован
Активность