Обновить
20.18

Проектирование и рефакторинг *

Реорганизация кода

Сначала показывать
Порог рейтинга

5 Ошибок Рефакторинга

А ты надел каску перед рефакторингом ?
А ты надел каску перед рефакторингом ?
  1. Добавлять в рефакторинг улучшения
    Строго отделяйте рефакторинг т.е. преобразование кода из одной формы в другую с полным сохранением поведения от любых даже самых незначительных улучшений, оптимизаций, украшательства и тд и тп

  2. Делать один огромный коммит
    Делайте много коммитов, каждый на свой шаг рефакторинга. Рефакторинг это как ходьба по заминированному лабиринту, нужно обязательно записывать все ходы и иногда отступать на шаг или N шагов назад и искать другой путь.

  3. Рефакторить без промежуточных проверок
    Когда вдохновение несет и хочется "прибраться" и тут и там и везде и некогда останавливаться можно "пролететь поворот" и даже не один. Лучше всего делить рефакторинг на логические этапы. "Дешевые" по времени и ресурсу проверки можно и нужно запускать как можно чаще: компиляция, тесты, запуск приложение локально. Между крупными этапами желательно проводить регрессионное тестирование. И самое отличное поэтапный релиз рефакторинга, чтобы провести не только синтетические проверки, но самую важную "проверку продакшеном"

  4. Затягивать и долго не релизить рефакторинг
    Топ выбрасываний рефакторинга на моей практике происходило из за желания довести его до окончательного окончания, всё всё исправить, привести в идеальную симметрию и тд и тп. Чем дольше человек очищает код, пока параллельно идут продуктовые спринты, тем больше он несет накладных расходов(мержить то надо) и тем больше падает вероятность успешной интеграции ветки рефакторинга с основной и его успешного релиза.

  5. Не думать о запасном варианте
    Не смотря на все многоступенчатые системы проверки качества вашего кода всегда есть далеко не нулевая вероятность ошибки, особенно когда "наводишь порядок" в самом ключевом месте системы (а где еще как не в таких местах наводить порядок).
    В таких ситуация очень полезно оставлять запасной вариант, например флаг переключения на "абсолютно старый код", лучше всего налету без рестартов.

    В своем канале о разработке в стартапах делюсь опытом и рассказываю еще больше удачных примеров и факапов. Буду рад видеть каждого! Заходите!

    Всем удачного рефакторинга!

Теги:
0
Комментарии0

Почему нужно использовать DTO

Data Transfer Object, термин, который для разработчиков на статических языках является чем-то самим разумеющимся, но вот остальные его могут не знать (даже если пользуются). Хотя в эпоху интеграций, фронтенд-бекенд, сервис-сервис, очереди, это крайне важная конструкция.

DTO это очень промежуточный объект между моделью в вашем коде и данными, которые вы отдаете наружу или принимаете от внешней системы.

  • Модель => DTO => json/protobuf/sql...

  • json/protobuf/sql... => DTO => Модель

Нафига? Почему не сразу преобразовывать из, допустим, json в нашу модель или наоборот? Тем более во всех экосистемах есть механизмы, которые позволяют упаковывать любые объекты, задавая правила преобразования через метаданные, аннотации или еще как-то. Пример из Java:

@Entity
public class User {
    @Id
    private Long id;
    @JsonIgnore              // приходится скрывать
    private String passwordHash;
    @JsonProperty("created_at")
    private LocalDateTime createdAt;

    // getters/setters ...
}

var json = new ObjectMapper().writeValueAsString(dto);

Существует масса причин, почему это плохая идея. Для начала, это банальное нарушение MVC архитектуры. Модель начинает знать как о представлении, о том какие поля надо выдавать наружу, какие нет, как их переименовывать и так далее. Если это кажется натянутым, то вот вам реальные последствия.

Одна и та же сущность для внешнего мира редко представляется одним способом. В зависимости от задачи, это может быть один набор полей или другой. Как это разрулить? Дальше, здесь плохо контролируется процесс, легко может быть такое, что новое поле автоматически попало наружу, хотя вы этого не планировали, но забыли его исключить. А если нужны вычисляемые поля или другое представление (всегда в датах)? В такой ситуации модель будет наполняться доп свойствами и методами, которые готовят доп данные для преобразования, что ведет к сильному загрязнению кода. Что из этого относится к бизнес-части, а что к представлению? Проблема.

DTO позволяют отделить представление от модели в коде, создавая по сути промежуточный слой. Имея его, вы можете независимо развивать свою модель и API для взаимодействия с ним. И да, это один из аспектов MVC, конкретно Model-View.

Готовые DTO гораздо легче чем модели конвертировать в типы на TS если у вас есть такая потребность. Например мы наши DTO (используем Alba), превращаем в типы TS с помощью готового инструмента (Typelizer). С моделями так легко не получится.

За это конечно придется заплатить. В проекте появится папка, с большим количеством файлов. Но это с лихвой компенсирует все описанные выше проблемы. DTO очень простые и для их создания далеко не всегда надо с нуля писать классы. В той же java они генерируются с помощью mapstruct, в других языках свои механизмы.

Но это только базовая история. Если мы еще подключаем инструменты генерации из sql (как в go) или openapi как везде, то те самые DTO создаются вообще автоматически на основе описаний.

INSERT INTO links (original_url, short_name)
VALUES (sqlc.arg(original_url), sqlc.arg(short_name))
RETURNING *;

DTO:

type CreateLinkParams struct {
	OriginalUrl string `json:"original_url"`
	ShortName   string `json:"short_name"`
}

Причем для update будет создана своя структура:

type UpdateLinkParams struct {
	OriginalUrl string `json:"original_url"`
	ShortName   string `json:"short_name"`
	ID          int64  `json:"id"`
}

Здесь отличается только id, но в реальных кейсах, отличий в создании или обновлении одной сущности обычно значительно больше, поэтому количество DTO тут становится еще больше.

DTO, кстати, должны быть имутабельны, иначе туда потечет логика

Больше про разработку в моем телеграм-канале Организованное программирование

Теги:
+6
Комментарии1

Я переписывался с товарищем на тему архитектуры и сформировал более цельное понимание построения ООП-кода :)

Чуть-чуть обо мне: в разработке 12+ лет, сделал 50+ различных приложений и ещё немножко игр, это только для бизнеса, плюс еще мои личные эксперименты и petproject-ы.

Делая все эти приложения я пришел к тому что есть разные подходы к проектированию\моделированию систем, в частности с помощью ООП-кода:

(Условно назову эти типы в игровой терминологии так как разговор был с человеком из геймдева и игры мне тоже близки, кстати, я и программировать то начал чтобы делать игры 😁, итак вот эти подходы: )

1. "Action" - когда ты описываешь объекты как будто смотришь на описываемую систему изнутри (вид из глаз), фокус на взаимодействие объектов между собой в моменте, моделируются в первую очередь сущности и упускаются процессы.

2. "Strategy" - когда ты описываешь объекты как будто смотришь на описываемую систему снаружи (вид сверху), фокус на процессы взаимодействия всех объектов в жизненном цикле приложения, моделируются в первую очередь процессы и упускаются сущности.

3. "God Mode" - когда ты делаешь все что душе угодно (читай как и то и другое, "Action" и "Strategy" в одном флаконе)

И так же я пришел к тому что есть несколько слоев моделирования\проектирования:

(как сказали бы в Теории Систем - есть надсистема, есть система и есть подсистема, а Шрек описал бы это как слои лука, а Осел рассказал бы что это напоминает слоёный торт 😁)

1. Уровень реальности в которой есть человек пользователь, используемый девайс (десктоп, ноутбук, смартфон, и т.п.), сервер с бэкендом; правила их взаимодействия (интерфейсы, протоколы, договоренности); и процессы в которых это взаимодействие происходит (циклы, цепочки событий, потоки данных)

2. Уровень приложения в котором есть сущности бизнес-логики(игровой логики), например Hero, Enemy, Obstacle, Menu, Map, Score; правила их взаимодействия; и процессы в которых происходит взаимодействие

3. Уровень инструментов в котором есть сущности языка программирования такие как примитивы(Int, Long, String, Bool), структуры данных, основные компоненты игрового движка(операционной системы); правила их взаимодействия; и процессы в которых происходит это взаимодействие

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

На каждом слое может использоваться как ООП подход так и ФП, так и нечто смешанное что добавляет удобства.

К чему это все? Наш разговор начался с того что я вбросил мнение что самая чистая архитектура обычно это чистейший ООП-код, и удобство архитектуры зависит как раз от навыка моделирования систем этим ООП-кодом.

Такие дела :)

Я по прежнему считаю что ООП рулит! 😁

А что думаешь ты про ООП, архитектуру и чистый код?

Делись своим опытом в комментариях, или нет..

Теги:
Всего голосов 6: ↑2 и ↓4-2
Комментарии19

Я исследовал тему связности и связанности в построении кода и вот к чему пришел:

Не существует плохих\хороших\идеальных связности и связанности кода.

Мне кажется проблема и решение глубже - сколько людей столько и вариантов осмысления и построения "модели", столько вариантов же coupling & cohesion. У каждого что-то свое.

Строить приложение от архитектуры - такое себе. Архитектура для приложения, а не приложение для архитектуры. Тогда архитектура будет основана на реальных задачах а не на поиске идеала.

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

Ведь суть всего этого моделирования чтобы "на понятном" объяснить железу что и когда нужно сделать.

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

Ну и когда я говорю ООП, это не значит что я буду писать абстракцию на каждый чих, влоть до Int, Long и т.п., нет, это значит что я начну с самых больших MyApp { UserClient, ServerClient, DeviceClient } и законтачу их между собой логикой приложения, а там дальше буду создавать абстракции по необходимости, если будет удобно и полезно что-то добавить и переиспользовать то я добавлю и переиспользую (вот кстати хороший критерий - моделировать сущность когда надо что-то передавать между главными абсракциями(надсистемами)).

ООП рулит :)

P.S. И не надо стремиться к идеалу, иначе тут можно скатиться в подмену задач, и начать делать не данное конкретное приложение, а предложенный кем-то идеал архитектуры.

Теги:
Всего голосов 4: ↑1 и ↓3-2
Комментарии9

Как правильно откатывать миграции? Если коротко, то никак.

В продакшене миграции могут идти только вперед. Какого? Откат миграции во время ролбека (при неудачном деплое) во-первых сильно усложняет всю процедуру, во-вторых, в теории, может ее некисло замедлить, уже не говоря про потенциальные локи на время отката. На фоне этого возможны ошибки, которые приведут всю систему в неконсистентное состояние.

Ролбек, в идеале, это просто переключение с одной версии кода на другую. Но ведь тогда возможны ошибки связанные с изменениями в базе? Если делать через жопу, то возможны. При правильном подходе, база всегда обратно совместима как минимум на одну версию. Только в этом случае мы можем обеспечить и бесшовный деплой (zero downtime deploy) и практически моментальный откат.

А это значит, что нельзя менять тип у колонок (если тип сужается), нельзя менять именования таблиц и полей. Если это все таки нужно, то существует немало техник, позволяющих сделать переход через создание новых сущностей и синхронизацией либо через код либо через саму базу (например с помощью триггеров). По этой теме даже написали целую книгу "Refactoring Databases: Evolutionary Database Design".

Получается, что любые ошибки в базе будут только накапливаться? Не совсем. Обратная совместимость обычно нужна только на текущую и следующую версию. Если у нас не коробка, а облачное решение, то одновременно могут работать только две версии. В таком случае, мы без проблем можем писать любые миграции, которые удаляют и меняют все что угодно, что уже не используется. Заметьте, это не откат, а новые миграции.

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

Больше про разработку в моем телеграм-канале организованное программирование

Теги:
Всего голосов 8: ↑6 и ↓2+5
Комментарии2

Выводим Бугаенко на чистую воду разбирая ООП

Топ Перлов

  • Любой массив байт должен уметь работать с файлами, сетью и тд.

  • Программа должна не падать на ошибках, а продолжать работу с фейковыми объектами.

  • Вместо падения в моменте конструирования объекта, надо падать на другом конце программы при каждом его использовании.

  • Я придумал новый язык, и чтобы он не так сильно тормозил, надо встроить GC в CPU.

Упомянутые ссылки

Копилка благодарностей

Теги:
Всего голосов 13: ↑5 и ↓8-3
Комментарии2

мои способы работы с AI контекстом (субъективное мнение):

  1. пользоваться общеизвестными терминами которые точно есть в словарях

  2. пользоваться только английским для кода, prd и дизайна

  3. сокращать код - как показывает практика файлы больше 500-600 строк агенту труднее прочитать. Тут есть несколько стратегий:

  • пользоваться препроцессорами, например можно сильно срезать времени если вместо JSX/TSX/html использовать pug

  • пользоваться и писать библиотеки. есть проблема - оформляем в абстрактную библиотеку, пишем доку и readme

  • предвидеть контекст - часть текста просто можно не писать, а просить догенерить, потому что получится эффект снежного кома, начинаем катить, смотрим куда катится - сокращаем

  1. меняем JSON на YAML или что-то близкое - это срежет кууучу лишнего хлама в виде ; }{ "" и прочих символов

  2. генерим code maps - mermaid и другие виды диаграм

  3. ANSII - визуализация простых идей, но не все модели хорошо работают с этим форматом, есть проблемы с layout. например я пользуюсь gemini

  4. подстраивать архитектуру и код под задачи и бизнес, не наоборот. Developer | Designer | AI as User.

  5. разбивать на мелкие подприложения если нужно

Надеюсь, что пост окажется полезным :-)

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

Спасибо за ваше время и хорошего дня!

Теги:
Всего голосов 6: ↑2 и ↓4-2
Комментарии0

мои способы работы с AI контекстом (субъективное мнение):

  1. пользоваться общеизвестными терминами которые точно есть в словарях

  2. пользоваться только английским для кода, prd и дизайна

  3. сокращать код - как показывает практика файлы больше 500-600 строк агенту труднее прочитать. Тут есть несколько стратегий:

  • пользоваться препроцессорами, например можно сильно срезать времени если вместо JSX/TSX/html использовать pug

  • пользоваться и писать библиотеки. есть проблема - оформляем в абстрактную библиотеку, пишем доку и readme

  • предвидеть контекст - часть текста просто можно не писать, а просить догенерить, потому что получится эффект снежного кома, начинаем катить, смотрим куда катится - сокращаем

  1. меняем JSON на YAML или что-то близкое - это срежет кууучу лишнего хлама в виде ; }{ "" и прочих символов

  2. генерим code maps - mermaid и другие виды диаграм

  3. ANSII - визуализация простых идей, но не модели хорошо работают с этим форматом, есть проблемы с layout

  4. подстраивать архитектуру и код под задачи и бизнес, не наоборот. Developer | Designer | AI as User.

  5. разбивать на мелкие подприложения если нужно

Надеюсь, что пост окажется полезным :-)

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

Спасибо за ваше время и хорошего дня!

Теги:
Всего голосов 5: ↑2 и ↓3-1
Комментарии0

BPN vs MVM

В двух, приложенных к этому посту файлах (здесь и здесь), показан код для решения одной и той же задачи в мобильном приложении. А именно: запустить обратный отсчёт перед началом игры.

В одном файле эта задача реализована в архитектуре BPN (Business Process Notation), о которой рассказывал раньше здесь. А во втором файле тот же код организован по архитектуре MVVM.

Код и в том, и в другом файле написан с помощью Claude Sonnet. В случае с BPN структурировал код вручную, следуя бизнес-процессам. А во втором случае попросил Клода сделать рефакторинг, используя традиционный современный подход и он выбрал MVVM.

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

Объём кода

В BPN варианте 270 строки кода с комментариями, в MVVM - 524.

Т.е., в MVVM случае кода практически в 2 раза больше.

Количество сущностей, объектов.

BPN - один класс и 3 раширения к нему.

MVM - 6 классов, 1 структура, 3 протокола, каллбэки, фабрика, расширение.

Архитектура

BPN - монолит.

MVVM - вью и модель, анимация и аудио как сервисы, роутер, отдельная структура для хранения значений свойств и т.д.

Что лучше

Как всегда, каждый из подходов имеет свои плюсы и минусы.

В BPN нравится, что можно видеть модель процесса, в данном случае модель одной из задач приложения.

Что такое “Модель”

Наиболее традиционны 2 понимания термина “модель”.

В одном случае, это структура данных, модель объекта.

Например:

struct Person {

let firstName: String

let lastName: String

var age: Int

}

В другом случае, под моделью понимается всё, что не относится к интерфейсу.

Но есть и третье понимание модели - это модель приложения, или модель отдельных процессов внутри приложения. Т.е., составные части приложения (процесса) и их последовательность.

В BPN файле такая модель проступает наглядно:

Модель процесса "Обратный отсчет"
Модель процесса "Обратный отсчет"

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

В файале MVVM блоков кода гораздо больше и нужно несколько раз проскоролить чтобы увидеть их в выпадающем меню. И увидеть целостную модель здесь сложнее.

Conclusion

На относительно небольших проектах архитектура MVVM может быть избыточна. Здесь могут использоваться более простые варианты.

BPN позволяет видеть целостную модель задачи (процесса, приложения).

Теги:
Рейтинг0
Комментарии10

Выводим Соера на чистую воду разбирая дискуссию с ним про принципы SOLID

Топ перлов

  • Если ты манки-патчишь объекты, то ты функциональщик.

  • Ты должен сначала залезть на гору, а потом уже решить надо было тебе сюда или нет.

  • Если люди по разному воспринимают принцип - это здорово, ведь он подталкивает людей к размышлению.

  • SOLID позволяет легче (т.е. не задумываясь) принимать не идеальные (т.е. сомнительные) решения.

Упомянутые материалы

Копилка благодарностей

Теги:
Всего голосов 10: ↑4 и ↓6-2
Комментарии0

Хочу поделиться с вами видением хорошей архитектуры Go проекта, к которой я пришёл на данный момент и также интересно послушать ваши варианты и мнения по данному поводу.

Моё видение:

У нас ядро приложения это сервисный слой (юзкейсы), именно ядро самая основная часть приложения, которая взаимодействует с какой‑то логикой.

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

Так как мы не хотим, чтобы логика из ядра уходила во внешние компоненты (по моему мнению, это повлечет переплетение зависимостей и нарушение принципов разделения ответственности), то вся логика должна выполняться на уровне ядра (например, вместо default значений полей в базе, мы создаём модель на уровне ядра, а база служит лишь в качестве хранилища, не выполняя какой‑либо бизнес логики). То есть бизнес‑валидация (инварианты агрегатов) остаётся в ядре, а адаптеры проводят schema‑валидацию (обязательные поля и форматы) до передачи в юзкейсы, тем самым мы избегаем лишних вызовов ядра (при некорректных данных), и не засоряя само ядро валидацией (отличным примером служит то, когда HTTP адаптер валидирует модель до передачи её в юзкейсы).

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

То есть я считаю идеальной архитектурой для большинства Go проектов, работающих на основании адаптеров (к примеру, REST или gRPC сервис) гексагональную архитектуру с включением подхода DDD.

У меня остались также холиварные вопросы к вам. Как считаете:

  • Передавать в юзкейсы структуру или поля по отдельности?

  • Должно ли хранилище, в виде БД например, иметь валидацию данных? Операции по крону? Дефолтные значения полей?

  • Транзакции: где их начинать/заканчивать?

  • Когда и где вводить versioning: в HTTP‑уровне, в домене (разные агрегаты) или в репозиториях (Multi‑tenant)?

  • Должны ли доменные ошибки возвращать rich‑error (с кодом/контекстом) или достаточно обычных error с текстом?

Теги:
Всего голосов 3: ↑2 и ↓1+1
Комментарии3

Вы знаете этих людей. Вроде умные, вроде опытные. Говорят правильные слова про 'стратегию', 'масштабируемость', 'долгосрочную перспективу'.
Но почему-то через полгода таких разговоров оказывается, что:

баг, который 'уже почти пофиксили' никуда из прода не девался
фича, которую 'вот-вот запустим' — всё ещё в черновиках
команда уже тихо ненавидит слово 'архитектура'

А техлид? Техлид как будто ничего не замечает.
Как это работает (точнее, не работает)
Слова вместо кода

вместо пулл-реквестов - диаграммы.
демо нет - зато вот вам слайды.
вместо решений 'опять' 'давайте обсудим' (читай: 'я не хочу отвечать').

Бесконечный 'анализ'

'Надо подумать над архитектурой' = 'Я не уверен, но боюсь признаться'
'Это нетривиальная задача' = 'Мне лень разбираться'

Ответственность - это не про нас
Любимый приём - щедро размазать вину:

'Это комплексная проблема' (на самом деле: 'виноваты все, а значит — никто').

Реальный кейс (чтобы было не абстрактно)

В одном проекте (Node.js, если важно) техлид 2 месяца 'прорабатывал подход' к рефакторингу.
Провёл 8 митингов, написал 50 страниц документации.

А потом... уволился.

Оставив после себя:
красивые схемы в Confluence
ни одной строчки кода
команду, которая теперь на рефакторинг смотреть не может

Как понять, что ваш техлид центральная часть системы самообмана?

главный результат его работы - не код, а презентации
коронный вопрос - 'А как мы это будем масштабировать?' (но не сам масштабирует)
после разговора с ним хочется или закодить, или закопать

Что прикажете с этим делать?

тупо запретить 'стратегировать' без кода*
нет пулл-реквеста - нет права говорить про архитектуру.

ввести 'день испанского стыда'
раз в месяц техлид показывает руками, что сделал. Не слайды - код.

Задавать всего один вопрос

'Что конкретно изменится после твоего решения?'
Если ответ начинается со слов 'теоретически....' - это тревога.

Вывод
Хороший техлид — не тот, кто красиво говорит о проблемах.
А тот, кто их решает.

Если ваш 'архитектор' только генерирует документы, но не генерирует код - возможно, он уже ИИ.

P.S. Если после этого текста кто-то узнал своего техлида - это не совпадение

Теги:
Всего голосов 6: ↑6 и ↓0+8
Комментарии4

Дели код на области (scope) - второй принцип. Полный список принципов здесь

Получил задачу, нужно подумать о области реализации. Иерархия областей такая: бизнес домен -> поддомен -> ... -> поддомен -> модуль(пакет) -> функция.

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

  • эпик содержит общее описание, покрывает несколько поддоменов, про модули тут ничего не понятно

  • история(сторя) в эпике описывает конкретный вариант использования, покрывает модуль поддомена. Если история покрывает больше одного модуля или поддомена, нужно подумать над декомпозицией.

  • техническая задача из истории - слой вниутри модуля на фронте или модуль на бэке.

Сore domain и всякие другие domain - это лирика-теория для аналитиков. Для разработчика это лишний информационных шум.

Здесь нужно быть внимательным, потому что:

  1. границы субъективны, зависят от понимания смысла, целей, отвественности компонента,

  2. для бэка и фронта границы областей проходят по разному в рамках одной системы:

    • для бэка поддомен->модуль совпадает с сервис->модуль/пакет или микросервис. Сервисы типа BFF и GraphQL - исключения, их можно считать частью глобального слоя контроллеров;

    • для фронта в слое контроллеров UI компоненты имеют свои границы, отличные от границ поддомен->модуль, но в остальных слоях совпадают.

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

Допустим, я разработчик бэка. Задача: сделать фасетный и текстовый поиск товаров. Есть сторя с описанием контракта. Тогда моя зона: поддомен -> модуль(пакет) -> функция модуля. Мысли такие:

  1. Поддомен уже понятен - обсудил в процессе декомпозиции эпика с аналитиком. Это будет Каталог. Сделаю его отдельным сервисом.

  2. Модуль примерно понятен: мне нужно сделать фасетный поиск. Скорее всего кроме поиска Каталог-сервис будет выдавать категории, продукты по отдельности и еще что-то. Фасетный поиск - модуль.

  3. Фасетный поиск должен возвращать значения для фасетов и результаты. Кажется, у модуля 2 функции. Может стоит сделать 2 модуля, или это будет 2 интерфейса внутри одного модуля? Решу, когда буду думать над логикой, там станет понятна связанность этих функций. Нужно будет обсудить с коллегами.

На фронте эта же задача выглядит по другому: это будет страница поиска, в ней фасетые фильтры, список товаров, страницы, сортировка. А еще где-то в шапке поле для ввода текста. Тут нет речи о Каталоге, потому что по смыслу это страница и какие-то элементы ввода. Это не страшно. Пусть структура UI компонент соответствует интуитивным ожиданиям фронт разработчиков. Важно, что бы ожидания были у всех одинаковые. Это в слое контроллеров.
В слое приложения лежит логика построения запроса по контракту. Вот здесь появляется Каталог, как поддомен, и модуль фасетного поиска. Позже тут будет модуль категорий и отдельного продукта. Слой контроллеров будет использовать их в разных UI компонентах.

Область и ограниченный контекст - разные вещи. Поэтому мы здесь его не обсуждаем.

Деление на слои и области выглядит как "решётка":

Весь код должен быть раскидан по ячейкам. Рано или поздно у вас появится общий для нескольких модулей код. Он хочет занять несколько ячеек по горизонтали. Скорее всего он выполняет общую служебную функцию внутри слоя. Здесь можно поступить по-разному: вынести в отдельный пакет, модуль с одним слоем, как договоритесь с коллегами.

Как физически организовать решетку и общие пакеты разберемся дальше.

Теги:
Всего голосов 2: ↑1 и ↓10
Комментарии1

Ближайшие события

Дели код на слои - первый принцип. Полный список принципов здесь

Я еще не знаю задачу, но уже знаю, что в коде будет 3 слоя: приложение, инфраструктура, контроллеры. Отдельно конфигурация.

Этого достаточно, ведь у меня даже нет задачи. Я начну думать о моделях, адаптерах, объекта‑значениях, когда приступлю обдумыванию бизнес логики и реализации.

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

На фронте слой содержит код, который хранит и управляет состоянием независимо от отображения: сохраняет состояние между переходами по страницам, дает доступ к состоянию из разных шаблонов и т. п.

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

— как модель, которые хранят состояние пока запрос обрабатывается;

— сервис без состояния

— функция с валидации целостности

Контроллеры — это все, что касается ввода и вывода.

Этот слой обязан знать, что нужно слою приложения, обязан это предоставить в нужном формате. Он не может ничего требовать от слоя приложения, обязан работать с тем, что отдал слой приложения.

На фронте в этот слой попадает всё, что касается взаимодействия с пользователем и обработки событий:

— шаблоны, стили, функции, которые готовят данные для отображения и обрабатывают ввод пользователя

— обработчики событий

— обработчики роутов, потому что роутинг — это интерфейс ввода данных.

— слушатели сокет соединений

На бэке это код, который принимает входящие запросы и отдает ответ, фоновые контроллеры:

— контроллеры gRPC, HTTP,

— слушатели очередей,

— конструкторы ответа, мапперы данных в нужный формат и т. п.

— описание протоколов.

Инфраструктура — помощник слоя приложения помогает получить, передать, сохранить информацию для выполнения бизнес логики.

Инфраструктура

— обязана работать с тем, что отдает слой приложения

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

Здесь лежат:

— клиенты к хранилищам, другим сервисам, кэшам, очередям и т. п.

— конструкторы запросов к БД, очередям или другим сервисам

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

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

Это может быть конфигурация di‑контейнера, место ручного создания компонен. Конфигурация может присутствовать как дополнительные нотации в UI компонентах. Фреймворки и языки имеют разные методы конфигурирования, поэтому конфигурация не является слоем или может быть не явной.

Слой приложения может отсутствовать. Тогда контроллеры и инфраструктура взаимодействуют напрямую. Чтобы сохранить порядок, нужно определить — кто главный. Главным лучше делать того, кто более стабилен.

На фронте слой контроллеров может часто меняться, контракт с бэком более стабилен — инфраструктура главная.

На бэке формат хранения может измениться после MVP, а контракт описан и стабилен — слой контроллеров главный.

Слои зависят друг от друга. Правила организации кода и внедрения зависимостей будут в отдельном посте.

Теги:
Всего голосов 6: ↑6 и ↓0+7
Комментарии0

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

Кто-то решит, что они вредные. Подумайте, прежде чем применять. Кому-то материал покажется очень простым, почти очевидным. Мой опыт показывает, что даже "бывалые" наводят бардак, хотя все всё понимают.

Принципы сформированы на основе практического применения информации из книг:

В оригиналах некоторые вещи трактуются объемно и широко.

Я про конкретику, практику и про код, поэтому сознательно упростил или выбросил часть информации. Она мешает и запутывает не только новичков, но и опытных разработчиков. Поэтому это не классический DDD или чистая архитектура. Скорее это смешение и личный опыт. Попытаюсь выдавать информацию без "воды".

Погнали:

  1. Дели код на слои

  2. Дели код на области (scope)

  3. Формируй структуру папок согласно делению на модули и слои

Теги:
Всего голосов 1: ↑1 и ↓0+1
Комментарии3

Полезные расширения для vsCode | просмотр JSON в виде дерева | Json Tree editor

Всем привет, этим постом начинаю серию постов с полезными плагинами для vsCode

🚀 Наименование: Json Tree editor

✍️Описание: расширение VSCode для визуализации данных JSON в виде дерева, с возможность выбора, перемещения и сортировки.

🔗 Ссылка на плагин: https://marketplace.visualstudio.com/items?itemName=GregChamblin.vscode-json-editor

Extension id: GregChamblin.vscode-json-editor

➕Плюсы:

  • Можно работать с большими файлами json

  • Есть отдельная кнопка на панели задач

  • При перетаскивании в дереве элементов, сам json также изменяется онлайн

➖Минусы:

  • Не выявлено

🚀Делитесь своими плагинами в комментариях.

#vsCode #JSON

Подписывайтесь на меня в телеграмм, там ещё много интересных плагинов @sa_chulan

Теги:
Всего голосов 2: ↑1 и ↓10
Комментарии0

Выводим Ситника на чистую воду

Топ перлов:

  • Sync-engine избавляет от однотипного кода по загрузке данных .. он заставляет вас проверять isLoading === true и рисовать крутилку.

  • Во всех sync-engine используются нормальные стейт менеджеры .. например, nanostore (см. видео с разбором этой библиотеки).

  • (Я запилил штуку, которая ничего не умеет, но ты можешь поверх этой штуки запилить своих костылей для решения проблем, которые у тебя возникнут из-за моей штуки).

  • CRDT - это просто лог операций (лог операций - это CmRDT и OT, CvRDT даже близко не лог).

  • Работать с IndexedDB через скомпилированный под WASM SQLite быстрее, чем напрямую работать с IndexedDB (разве что, если руки заточены под обнимашки).

Упомянутые ссылки:

Копилка благодарностей

Теги:
Всего голосов 4: ↑1 и ↓3-2
Комментарии1

VK (видео)

📦 API for Any(thing) 2

☝️Возможно ли создать интерфейс для получения любого объекта одинаковым способом? 

Библиотека работает на продакшене в приложениях:
Энергия
NFC Tool
КубГТУ

Во второй части доклада практическая реализация 💡

Хабр
Medium
GitHub

El-Machine.com Apps 🤖

Теория:
Часть 1

Теги:
Рейтинг0
Комментарии0

YouTube (видео)

📦 API for Any(thing) 

☝️Возможно ли создать интерфейс для получения любого объекта одинаковым способом? 

Продолжаю развивать свою идею архитектуры для 100% инкасуляции, разбития на модули и тестирования всего слоя Model

Хабр
Medium
GitHub

Первая часть доклада теоретическая. В поисках API для любого (Any) объекта

Во второй части доклада практическая реализация 💡

Поделитесь мыслями:
Что думаете про декларативны подход? Описываю результат и получаю нужный объект

Часть 2

Теги:
Рейтинг0
Комментарии0

Луковая архитектура, и как не заплакать в процессе погружения. Запись митапа

Суть лукового архитектурного подхода (Onion Architecture) — в разделении системы на несколько слоёв, каждый из которых выполняет свою функцию. Такой подход помогает сделать код более чистым и читаемым, обеспечивает удобное разделение бизнес-логики, домена приложения и инфраструктуры.

На внутреннем митапе рассказали, как устроена луковая архитектура, границах применимости и о том, как с пользой начать использовать её в разработке. Затронули теорию и практику: рассмотрели специфику луковой архитектуры на примере конкретного pet-проекта. 

Коснулись CQS-CQRS дизайн-паттерна, рассмотрели пайплайн MediatR, реализацию валидаций с FluentValidation, маппинг через AutoMapper, и как организовать сущности доменного слоя. 

Остановились на первых двух слоях — Domain.Core и Application. Об устройстве других слоев (Infrastructure и Web API) поговорим на будущих митапах.

Посмотреть запись митапа можно на YouTube или RUTUBE.

P.S. Это запись внутреннего митапа ИТ-команды Сравни — публикуем эпизоды без NDA, но (надеемся) с пользой для внешнего сообщества.

***

Узнавать о выходе наших новых материалов (митапов, лекций, статей), помимо Хабра, можно в тг-канале инженерного сообщества Сравни

Теги:
Всего голосов 10: ↑10 и ↓0+12
Комментарии0
1

Вклад авторов