Search
Write a publication
Pull to refresh
0
0
Send message

> Но давайте обсудим. Что существенного я упустил?

SR:

Есть метод для преобразования записи БД в строку и он используется при построении отчетов и при выводе в консоль. Разработчик добавил в этот метод системную информацию для консоли и у всех поехали отчеты. Изменения расползаются куда попало (от наследников к родительским классам, от реализаций к интерфейсам и т.п.)? Нет. Принцип нарушается? Да.

OC:

Есть метод с большим switch и периодически программисты приходят и дописывают туда свои кейсы. Изменения расползаются? Нет. Принцип нарушается? Да.

LS:

Делаем свою реализацию интерфейса List под названием DoubleAddList и реализуем метод add так, что при его вызове аргумент добавляется в список дважды. Изменения расползаются? Нет. Принцип нарушается? Да.

IS:

Делаем интерфейс System с сотней-другой методов для работы с функциями ОС и реализуем этот интерфейс для Android/Windows/iOS и т.д. Изменения расползаются? Нет. Принцип нарушается? Да.

DI:

Пишем свое приложение, которое зависит от конкретной реализации системы логирования. Как только меняем версию либы логирования, мы должны перетестировать свой модуль. Изменения расползаются? Нет. Принцип нарушается? Да.

И если все в этой благодатной атмосфере, то мнение против просто тонет в потоке поддакивания.

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

Дело не в руководстве с гнильцой, а общих заблуждениях о том, что работа выполнена и дальше всё равно как - лишь бы больше прибыли.

Тут думаю есть два варианта:

  1. Руководство осознает риски в долгосрочной перспективе угробить проект, но принимает их и просто качает деньги. Такой сценарий возможен и не сказал бы, что он совсем ужасен с т.з. бизнеса. Он отвратителен и неприятен до дрожи в коленях для IT отдела, что выливается в большую текучку кадров, но в то же время и поток денег никуда не пропадает, а зачастую и даже усиливается. В этом сценарии руководство играет в краткосрочную с перспективой либо разорвать отношения с заказчиком, либо начать разработку с нуля (опять же за доп. деньги от заказчика).

  2. Во втором сценарии руководство не осознает риски и считает, что архитектор - это пятое колесо в системе, которая вышла в прод и стабилизировалась. И вот тут я никак не могу назвать такое начальство компетентными людьми. Все равно что начальник больницы скажет, что хирургов в штате держать слишком дорого и пусть операции делают медсестры.

И вот на эти два случая может быть две точки зрения:

Архитектор: 1 и 2 - начальство с гнильцой, которое не дает выполнять свою работу;

Бизнес: 1 - удовлетворительная стратегия, которая приносит деньги. 2 - некомпетентное руководство, которому затмили глаза деньги (как в золотой антилопе). На первый взгляд между 1 и 2 разница небольшая, но во втором случае некомпетентность часто выражается и в других аспектах ведения бизнеса, что скорее всего рано или поздно приведет к краху компании.

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

Мне кажется такие люди есть на любом жизненном этапе проекта. Задача архитектора и руководства в том, чтобы таких людей вовремя останавливать. В нормальных проектах они обычно или перевоспитываются или увольняются. Если архитектора не слушают или вовсе увольняют, то тут конечно руководство с гнильцой и ничего с этим особо не поделать т.к. если в мозгу компании опухоль, то ожидать, что она когда-нибудь излечится слишком наивно.

3 разных API, вместо обновления и рефакторинга одного

Я про это же. Когда начинают в обход существующего API рядом пристраивать "такие же, но чуть другие" т.к. в старом или лень или страшно разбираться архитектура начинает разваливаться. В то же время если мы залоггируем как-то не так сообщение, то архитектура никак не пострадает.

Цикл статей довольно интересный, спасибо. Смутили только примеры кода разделе Золотые шестёрки.

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

Дабы не быть голословным:

Я думаю примеры высосаны из пальца. Можно написать лучше? Да (хотя с логгером я думаю и так все в порядке). Относятся ли эти примеры кода хоть на тысячную долю процента к архитектурным "стенам", которые сносят? Нет. Стены - это интерфейсы. Реализация этих интерфейсов может быть сколь угодно плохой, но если она инкапсулирована под хорошо спроектированным интерфейсом, то для неё а) можно написать хорошие автотесты б) в любой момент ЖЦ системы можно переписать эту реализацию на более красивую и/или эффективную.

Мы пытались использовать GraphQL в качестве API, но он не подошел по ряду причин:

  1. Обязательность схемы. Это очень сковывает и не дает динамически расширять атрибутивный состав сущностей на лету. У нас своя система типов, по которой мы можем узнать что именно можно получить из сущности, но мапить эту систему на схемы GraphQL - это боль. Мы частично смогли обойти ограничение по схеме через параметры, но это стало выглядеть очень страшно. Например, для получения полного наименования контрагента имея на руках ссылку на договор приходилось писать что-то вроде: {att(n:"counterparty"){att(n:"fullName"){str}}}. Но даже с этим подходом снова возникла проблема - чтобы загрузить несколько атрибутов таким образом нужно обязательно указывать псевдонимы т.к. иначе в GraphQL берется имя атрибута (в нашем случае "att", который у всех атрибутов по факту один и тот же). Пришлось городить генерацию псевдонимов ("a", "b", "c", ...) перед тем как отдать движку на вычисление наш запрос.

  2. На UI работать с результатами очень не просто если нет возможности описывать там бизнес-сущности (мы разрабатываем универсальную платформу и бизнес-сущности в основном коде UI - это непозволительная роскошь). Т.е. приходилось вручную работать с большой вложенностью результатов и бесконечными проверками на null/undefined. Lodash конечно облегчал жизнь, но даже с ним было очень не просто жить с таким API. Решение этой проблемы оказалось довольно тривиальным - перед тем как отдать результат клиенту, мы для всех объектов в дереве результатов где ключ только один возвращаем значение по этому ключу вместо объекта. Т.о. объект {"counterparty": {"fullName": {"str": "ООО Рога и копыта"}}} превращался в "ООО Рога и копыта";

  3. Как указали ранее - общение бэк <-> бэк на GraphQL довольно проблематично, а делать несколько наборов API для разных юзкейсов очень не хотелось;

  4. К GraphQL API очень напрашивались возможности, которые свойственны template движкам. Например, отформатировать полученную дату по заданному шаблону. Вернуть значение по умолчанию если атрибут вычислился в null (и много других). Стандарт этого сделать не позволяет;

  5. Отношение к сущностям и их идентификаторам в GraphQL очень поверхностное. Нам дают возможность добавлять параметры для атрибутов в query, но то что в них передается - это все уже забота разработчика и никаких оптимизаций от движка особо ждать не приходится. Наша попытка сделать универсальный атрибут для запросов любых сущностей из любых источников данных закончилась довольно грустно. Очень не хватало возможности вернуть из атрибута некоторую ссылку на другую сущность и дать движку уже самому решать как и что на основе этой ссылки загружать;

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

  7. Оптимизация. Реализация на java при загрузке {a: abc{def}, b: abc{hig}} по факту загружала "abc" дважды. Зачем дважды грузить одно и то же в readOnly запросе? Вопрос для знатоков.

Но справедливости ради в GraphQL API есть очень весомые плюсы:

  1. Клиент точно знает тип результатов, которые он получит из запроса;

  2. Нет underfetching/overfetching проблем (как в этой статье и описывается);

В итоге мы реализовали свое API с теми же плюсами, но без минусов, которые описаны выше. Для той же задачи загрузки плного имени контрагента мы имеем:

API.get(contract_ref).load("counterparty.fullName?str!'unknown'")

а на выходе или 'unknown' или 'ООО "Рога и копыта"'

Для поиска записей:

API.query({"sourceId": "contracts"}, "counterparty.fullName?str!'unknown'")

и получаем уже массив из имен контрагентов для всех контрактов

Мы разве не можем на собственном почтовом сервере сделать фильтр вложений, который разрешает передавать файлы без валидации только от коллег, письма которых подписаны ЭЦП? Для внешних отправителей можно сделать своего рода фильтр, который все вложения загружает на внутренний ресурс и вместо вложения в письме формирует ссылку на этот ресурс. При переходе по ссылке мы попадаем в зону, которая требует апрува от безопасников. Если файл признается безопасным, то мы безпрепятственно его качаем. Пока апрува не получено пользователь будет видеть сообщение "Файл на рассмотрении. Для эскалации звоните туда-то туда-то".

Рисунок 9. Один и тот же запрос с разными псевдонимами.

Я думал это недоработка реализации GraphQL API в java, но видимо проблема куда шире. У нас по сути ReadOnly древовидный запрос атрибутов, из которого можно выкинуть все псевдонимы перед вычислением (объединить {aa: abc{def}} и bb: abc{hij} сделав abc{def,hij}) и только получив все нужные данные вернуть их клиенту со всеми псевдонимами. Странно, что этого нет по умолчанию во всех реализациях GraphQL API.

Можно ли защититься от фишинга полностью?

Нет, и это факт, с которым стоит смириться. «Человеческий фактор» – не просто словосочетание, это основная движущая сила фишинговых атак.

Разве использование ключей, которые пользователь физически не может ни в какую фишинговую форму ввести (эцп для почты или доступ в интранет по токену) не спасает от подобных атак в 99.9% случаев?

Согласен. Все рутинные операции программистов решаются написанием универсальных библиотек и фреймворков. Если задача не решается библиотекой, то и нейронкой её скорее всего никак не решить.

Не совсем так. Функция из одной строчки может нарушать SRP, а класс из тысячи строк может не нарушать. Дробление на атомы это другой принцип и Мартин об этом говорит в книге "Чистая архитектура":

Примером нарушения принципа может быть утилитная функция

String toPrettyString(Person person)

Которую используют для преобразования Person в строку (имя + фамилия например). Функцию используют при выводе в лог и при построении отчетов. В один прекрасный момент в логах захотели вывести еще и UUID пользователя и поменяли функцию, а следом поехали все отчеты, которые её использовали.

1. Слишком мелкая детализация ответственностей

Ерунду написали об SRP. Принцип не о том, что нужно дробить весь код на атомы, а о том, что у одного модуля (функции, класса или файла с исходным кодом) не было разных интересантов для внесения изменений.

Information

Rating
7,969-th
Registered
Activity

Specialization

Specialist
Lead
Java
Java Spring Framework
Kotlin