Архитектура приложения

Підписуйтеся на Telegram-канал «DOU #tech», щоб не пропустити нові технічні статті

Доброе времени суток. Нужен совет сообщества.

Заинтересовался вопросом развития навыков построения архитектуры фронтэнд приложения.
Источников есть много: Дядя Боб, Lean architecture etc.

Может еще какие книги или статьи посоветуете? Спасибо.

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному1
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Это называется software design. Архитектура это другое: stackoverflow.com/...​-vs-software-architecture

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

Не совсем понял про какое «это» Вы пишете :) Исходя из ответа на SO я спрашиваю таки про архитектуру. А как Вы определили, что я про дизайн спрашиваю? ;)

Дедукция, Сэр.

развития навыков построения архитектуры фронтэнд приложения

 — какая у фронта архитектура может быть? Организация кода — да. Стейт менеджмент это не архитектура если шо.

Тогда может по Software Design посоветуете что-то почитать?

Вот, может быть полезно www.educative.io/...​01419520/5649050225344512.

А на UI я бы смотрел в торону композитного UI. Книги не посоветую, но вот есть jimmybogard.com/...​r-microservices-a-primer. И еще можно найти видео с NDC.

UPD: Оно прям в комментах есть vimeo.com/223985688.

Вероятно, «архитектуры фронтэнд приложения» — это не совсем корректная формулировка. Приложение (система, решение), ведь, не только из фронт-энда состоит.

Вполне корректная формулировка. Приложение/система состоит из совокупности приложений/систем которые между собой взаимодействуют.

Завтра, 11 декабря, намечается такое онлайн-мероприятие. Может будет интересно
m.habr.com/...​ompany/avito/blog/432036

Вообще-то нет разницы фронтенд это приложение или ембеддед, архитектура это не об этом

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

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

Имеет место быть. Можно еще говорить, что архитектура — это о взаимосвязи компонентов/модулей решения/приложения/системы. Ведь по сути приложение — это система модулей/компонентов, из которых оно состоит.

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

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

согласен что понятнее, только как по мне это не самый лучший путь

Не знаю что такое

Lean architecture

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

Clean architecture. А почему бежать нужно то?

Clean architecture.

А разве не имелось ввиду именно Lean Architecture? Я слышал про такое, но не сталкивался.

А почему бежать нужно то?

Потому что солид и дизайн паттерны — это вредные в 99% случаев использования концепции.

Конечно спорное. Людей успешно оболванивали лет 30, что надо слоеные архитектуры городить на ровном месте не понимая для чего и плодить тучи классов передавая из слоя в слой. Никто уже и не спрашивает почему нужно каждый слой выносить в отдельную либу — просто как обезьяны выносят.

Людей успешно оболванивали лет 30

Какой кошмар! ВЫ давно вайти?

Я неоднократно сталкивался с кодом, в которых всех этих паттернов не было. Вкратце — лучше бы были. Правда.

То просто язык такой наверное, обычно паттерны ООП тулят там где язык не умеет в простые вещи, что бы можно было писать простой и понятный код, а надо было даже простые вещи повторять по многу раз или писать кучу boilerplate кода.

Какой язык умеет в простые вещи?

Таких языков достаточно много, под соотвествующие задачи. Но если говорит про типичные ооп языков то они со своими парадигмами и партернами уже давно развиваются в идеологию функциональных — максимально замешают statement based синтаксис expressions, стараются уменьшать количество сайд эффектов, отвергают непрямые входы (привет dependency rejection), все чаще по коду встречаешь не ооп шные подходы в ооп языках, общий дизайн даже приложений отвергает оопшный inheritance в пользу более гибкой композиции и тд. Так что в целом да — ничего спорного тут нет, сколько не городи ооп абстракций, boilerplate кода меньше не становиться.

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

которые вы решаете в ооп эффективней? например?

Моделирование систем реального времени например. Без состояний это сделать чудовищно сложно. Я знаю про Mutable и Монады Состояний, но это — костыли. А единственный трушный способ хранить состояние в ФП — это стек (через рекурсию).

Не стоит впадать в фанатизм. ООП и ФП — это инструменты. Один инструмент лучше подходит для одного, другой — для другого.

А единственный трушный способ хранить состояние в ФП — это стек (через рекурсию).

Ну возьмите не трушный язык — а на котором удобно решать ваши задачи и умеет в mutable. Кроме Хаскеля вокруг есть множество мультипарадигменных языков, где задачи решают намного эффективней чем их может решить ООП.

Я знаю про Mutable и Монады Состояний, но это — костыли

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

Но это такое — показателем качества здесь есть неизменчивость концепции, в ФП они как были 30 лет так и живут сейчас, а что осталось от ооп и паттернов ГОФа например — когда там все паттерны строились на непрямых входах в классах, а тут внезапно как гром среди ясного неба передавать зависимости через конструктор в 201* стало тотальным антипаттерном и всякие Марки Зиманы начали кормить всех тем как это плохо и петь про dependency rejection.

Самое главное, что ФП языки живут без всего этого успешно и решают те же задачи эффективней и безопасней — возьмите F# и сравните с C#, в F# ООП доступен но никому не нужен.

ГоФ вполне актуален по сей день. Значительная их часть внедрилась в языки. Как и DI: это проще, чем возится с инстанциированием вручную.

Чистых ФП языке в мейнстриме не встретишь: повторяюсь, многие вещи на них делаются черезвычайно муторно. Почти все серьёзные языки сейчас мультипарадигменные и это правильно: выбираешь инструмент под задачу. А крайности оставьте фанатикам.

Ну возьмите не трушный язык — а на котором удобно решать ваши задачи и умеет в mutable.

Мы возвращаемся к императивщине. А потом мне пнадобится инкупсуляция и мы таки доберёмся до ООП)

в F# ООП доступен но никому не нужен.

F# никому не нужен к сожалению. В той же Скале ООП доступен и используется.

Чистых ФП языке в мейнстриме не встретишь: повторяюсь, многие вещи на них делаются черезвычайно муторно.

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

ООП паттерны работают так себе — я сталкиваюсь с ООП каждый день на множестве проектов вижу как люди в этой кухне варятся, даже простые вещи интерпретируют кто как хочет, придумывают по 100500 интерпретаций этих несчастных паттернов, заучивают их, спорят чей синглтон более «трушный», а на выходе получают тонны boilerplate кода, который не делает ничего полезного и все ради чего — сделать простую расширяемость и убрать дублирование кода, а на выходе как ловили nullreference exception 20 лет назад повсеместно, так и продолжают это делать, ничего умней знака вопроса придумать не смогли, зато синглтон сделали canonical — просто лол.

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

Я привёл такой пример, а уж про мутабельность как основное отличие вы сами выдумали. Отличий полно. Но судя по всему, никого кроме себя вы слушать не намерены, так что напрягаться больше не буду.

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

Если пользуются — это уже не фп. Повторяю ещё раз, есть только 2 валидных фпшных способа работать с состояниями: монада состояний и рекурсия.

возьмем опять же с# далеко ходить не надо, тут последние годы никаким ООП и не пахнет — локальные функции, тюплы, деконструкция, паттерн матчинг, explicit nullables — все идеи из ФП.

ООП у него от рождения, что туда добавлять, множественное наследование что ли? А добавлять что то надо, вот и добавляют. Оно, кстати, так сяк приживается, а большинство C# программистов до сих пор про новый синтаксис кортежей не слышали.

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

Часть паттернов эмулирует отсутствующие в чистых ООП языках функциональные возможности, так что возможно всё. Другое дело, какой ценой. Я не по наслышке знаком с ФП на примере того же F# и по собственному опыту знаю, что отказ от ООП приводит к невероятно уродливым аберрациям, когда нужна банальная инкапсуляция или всё то же постоянное изменение состояний. Это просто разные инструменты. Не плохой и хороший, а разные.

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

Почему вы вообще сравниваете ООП с ФП? Эти понятия лежат в разных плоскостях и не являются взаимоисключающими.

Это сухая теория. На практике ФП подходы выдавливают ООП там, где он себя вроде как чувствовал натурально и для чего создавался — реализация Domain specific вещей не говоря уже про написание алгоритмов, работу со структурами данных, программирование стейт машин и тому подобное. Если следить за гитхабом и популярным репозиториями, кроме java где все эти ООП подходы и паттерны выродились в насмешливые истории об неймингах такиих абстракций, все понимают и оценивают критически стоимость такого дизайна и пользу от него, поэтому используют там где оно наверняка подходит и не следуют во многих случаях, оптимизируя же прибегают к уменьшению количества абстракций почти всегда, которые в ООП очень любят.

На практике они спокойно существуют вместе, решают разные задачи и никто никого не выдавливает.

Громкие слова. На практике в индустрии ФП живёт исключительно как синтаксический сахар в ООП языках. Sad but true.

Довольно часто на DDD конференциях говорят о моделировании предметной области с использованием функциональных языков.

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

Разумеется, в оверинжениринге нет ничего хорошего, так же нет ничего хорошего в КИСС. Истина — посредине)

Да простая: оно хорошо только для продуктов, которые пилишь один раз и навсегда. Иначе любое изменение/расширение потенциально тянет кучу переделок.

Усложнение базовой архитектуры может значительно упростить расширение и модификации.

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

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

2 + 3 — 4

Используя FP мне нужно отделить состояние от операций и каждый раз матчить что это такое и только потом выполнять с ним заточеную именно под этот тип операцию.
Используя ООП мне нужно только предоставить контракты листа и узла. Опционально — корня. Сказать так же что каждый из этих контрактов — Printable. Далее — как ходить по дереву и как печатать — говорит уже сам узел. Ты ему только отдаёшь команду.

После — когда появляется новое требование (например об операци деления в этом дереве) — реализовавшему его в FP — стиле нужно будет менять код — вносить новый тип в тот здоровый match-case, который выбирает специфическую для типа операцию.
реализовавшему же изначальное дерево в ООП стиле достаточно предоставить ещё одну реализацию — узел операции деления и всё будет работать.
А теперь представьте что изначальный код ещё и находится не под вашим контролем (у вас физически нет возможности расширить тот match-case) что делать FP — разработчику? Только копировать всю либу полностью (в лучшем случае форкать).

Подобый примитивный пример приводит Дядя Боб (так и не понял почему его здесь не любят) и подводит под него бизнес-кейс парой минут позже.

Разумеется, сразу можно привести пример что если кроме операции печати выражения (рисования форм в примере Дяди Боба) вдруг появится требование новой операции, например eval выполняющее выражение (describe shape в примере Дяди Боба) — то всем будет одинаково плохо и больно вносить туда изменения. Но на то это и изменение контракта. Но и на это как в FP так и в OOP есть решения.

Да и выражение простое как

2 + 3 — 4
Используя FP мне нужно отделить состояние от операций и каждый раз матчить что это такое и только потом выполнять с ним заточеную именно под этот тип операцию.
Используя ООП мне нужно только предоставить контракты листа и узла. Опционально — корня. Сказать так же что каждый из этих контрактов — Printable. Далее — как ходить по дереву и как печатать — говорит уже сам узел. Ты ему только отдаёшь команду.

А теперь посмотрим как реально обстоят дела

ООП подход




class Lit implements Exp {
    int n;
    int eval() { return n; }
}


class Add implements Exp {
    Exp x, y;
    int eval() { return x.eval() + y.eval(); }
}


class Mul implements Exp {
    Exp x, y;
    int eval() { return x.eval() * y.eval(); }
}

//Добавляеться новая операция 

class Add implements Exp {
    Exp x, y;
    int eval() { return x.eval() + y.eval(); }
    string toString() { return x.eval() + "+" + y.eval(); } // Доп код 
}


class Mul implements Exp {
    Exp x, y;
    int eval() { return x.eval() * y.eval(); }
    string toString() { return x.eval() + "*" + y.eval(); } // Доп код 
}


функциональный подход

type Operation = 
    | Add  
    | Multiply

type Head = 
    | Op of Operation 
    | Lit of int

type Node = 
    | Val of  Node * Head * Node  
    | None 

let rec eval tree =
    match tree with 
        | Val (right, head, left) ->
             match head with 
                | Op Add -> eval(right) + eval(left)
                | Op Multiply -> eval(right) * eval(left) //<--- Добавляеться новая операция 
                | Lit i -> i 
        | None -> 0

let rec toString tree =
    match tree with 
        | Val (right, head, left) ->
             match head with 
                | Op Add -> toString(right) + " + " + toString(left)
                | Op Multiply -> toString(right) + " * " + toString(left) //<--- Добавляеться новая операция 
                | Lit i -> i.ToString() 
        | None -> ""

Добавляем немного магии, которая в java на текущий момент недоступна.

 

let expFun right operation left  =
        let getNode arg = match box arg with 
                            | :? int as intNode ->
                                    Val (None, Lit intNode, None) 
                            | :? Node as node -> 
                                node
                            | _ ->
                                Node.None
        Val (getNode right, Op operation, getNode left)

let (+) a b = expFun a Add b
let (*) a b = expFun a Multiply b

И теперь самое интересное — как выглядите вызов этот ООПшного кода (упускаю кучу того boilerplate кода что бы эти фабрики ГОФ завелись) из вашего же примера в статье как одного из самых передовых достижений!

<T> T e1(ExpAlg<T> f) {
    return f.add(
        f.lit(1),
        f.add(
            f.lit(2),
            f.lit(3)));
}

И на функциональном языке

let resStr = toString (1 + 2 * 4)
let resEval = eval (1 + 2 * 4 + 3)

Output: 
val resStr : string = "1 + 2 * 4"
val resEval : int = 12

Если добавить еще проверки на null pointer exception ввезде будет еще больший вынос мозга, в функциональном примере это уже покрыто.

Спасибо — за такие достижения ГоФ и ООП, лучше все это делать «негибком ФП», как видно из примера.

Перечитайте пожалуйста исходный коммент. Он не о том что задачу можно решить в меньше строк кода. Он о примитивной задаче модульности. Я же даже упрощённый бизнес-кейс привёл с задачей продажи по модулям. Даже с таймингом чтоб ленивым не надо было искать его во всем полуторачасовом докладе.
И если в OOP эта задача решается элементарно то в FP на столько тривиального решения нет — ну не можете вы ничего добавить без добавления case’а в match. И да, решение может быть, но оно не на столько тривиально как при использовании ООП.
Далее, в вашем же примере вы выстроили себе ловушку

Val (right, head, left)

как она себя будет вести при добавлении унарной операции типа квадратного корня? Так же как Lit — тоже добавлением кастомного кейса? А правильно ли это? Очень вряд ли — оно ведь операция, хоть и унарная.

И нет, я предпочитаю не использовать Object algebras даже на Scala.
Статью япривёл для того чтоб показать что даже на столько тяжёлая вещь как изменение контракта — решается. Да, не тривиально, но решается.

Допустим примитивный пример: есть структура описвающая выражение в виде бинарного дерева где узлами являются операции, а листьями — константы. Базово есть задача тьлько печатать такое выражение. Да и выражение простое как
2 + 3 — 4
как она себя будет вести при добавлении унарной операции типа квадратного корня?

Будет вести себя так как, ведет бинарное дерево, на котором описывают унарную операцию. Это проблема не ФП.

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

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

Вообще пример выбран очень неудачный — работа с деревьями выражений это задача которая абсолютно натуральная для ФП и совершенно уродски выглядит на любом ООП языке да еще и сделанная на ГОФ паттернах. Вы много знаете языков программирования реализованных с применением ГОФ паттернов или ООП?

Смешная шутка. Как пример IEnumerable в C# - это Итератор из GoF.

не знал, что IEnumerable это язык программирования.

Ты просил пример языка с паттернами GoF — я тебе привёл пример такого языка. Признай очевидное и спи спокойно.

Очевидно, что у кого-то большие проблемы с абстрактным мышлением и логикой.

Умей признавать свои ошибки. Большинство мейнстримовых языков так или иначе реализуют те или иные GoF паттерны. В C# есть итератор, интерпретатор, прототип. В Руби — есть даже синглтон. Я понимаю, что ты живёшь в каком то своём маня мирки, где ФП внезапно всех победило, но жестокая реальность несколько иная.

Будет вести себя так как, ведет бинарное дерево, на котором описывают унарную операцию. Это проблема не ФП.

Разумеется. У FP проблем нет, только у определения способа его использования.
Рассмотрим далее — в корневом модуле, определяя типы, вы говорите что

  • Operation — это только Add и Multiply
  • Head — это только операция из описаных выше или литерал
  • Node — это структура из трёх (хардкожено) элементов или ничто
И ладно с match хоть как-то можно работать (даже не учитывая что при выборе ооп подхода это сделать проще). Например расширив его через конструкцию-аналог orElse в Scala (хаскель я не знаю, но он не далеко, буквально на пару этажей выше, ушёл от Scala в ivory tower). Но как вы
  • добавите новую операцию в дочернем модуле без переопределения типа в родительском?
  • скажете что вы можете матчить не по типу (типу, не его варианту) с тремя элементами, а с двумя — операция и значение?
Пример специально выбран так ведь, здесь я с вами полностью согласен, деревья лучше и проще обрабатывать в функциональном стиле, но стоит лишь выйти из универсума математики спустившись на грешную землю и столкнувшись с простейшей практической задачей — все предыдущие труды канут в лету. При выборе же OOP подхода там где нам заранее не известно всё множество вариаций — мы получаем более гибкую систему с сохранением предыдущих наработок.
Но как выдобавите новую операцию в дочернем модуле без переопределения типа в родительском?
скажете что вы можете матчить не по типу (типу, не его варианту) с тремя элементами, а с двумя — операция и значение?

Вот пример полный c поддержкой обоих пунктов на ФП.

type IBinaryExpOp = 
    abstract member ToStr : string -> string -> string
    abstract member Eval : int -> int -> int


type BinOp = 
    | Add  
    | Multiply 
    | ExternalBinOperator of IBinaryExpOp   

type UnOp = 
    | Increment
       
type Head = 
    | BinOp of BinOp
    | UnOp of UnOp
    | Lit of int

type Node = 
    | ValBinary of  Node * Head * Node      
    | ValUnary of  Node * Head           
    | None 
    

let rec eval tree =
    match tree with 
        | ValBinary (right, head, left) ->
             match head with 
                | BinOp op -> match op with  
                                 | Add -> eval right + eval left
                                 | Multiply -> eval right * eval left
                                 | ExternalBinOperator ext -> ext.Eval (eval right) (eval left)
                | Lit i -> i 
                | _ -> failwith "Unary exp with binary node."
        | ValUnary (node, head) -> 
             match head  with
               | UnOp Increment -> eval(node) + 1
               | Lit i -> i 
               | _ -> failwith "Binary exp with unary node."
        | None -> 0


let rec toString tree =
    match tree with 
        | ValBinary (right, head, left) ->
             match head with 
               | BinOp op -> match op with  
                                 | Add -> toString right + " + " + toString left
                                 | Multiply -> toString right + " * " + toString left
                                 | ExternalBinOperator ext -> 
                                    ext.ToStr (toString left) (toString right) 
                | Lit i -> i.ToString() 
                | _ -> failwith "Unary exp with binary node."
        | ValUnary (node, head) -> 
              match head  with
                | UnOp Increment -> "++" + toString(node) 
                | Lit i -> i.ToString() 
                | _ -> failwith "Binary exp with unary node."
        | None -> ""

let expFunBin right operation left =

        let getNode arg = match box arg with 
                            | :? int as intNode ->
                                    ValBinary (None, Lit intNode, None)
                            | :? Node as node -> 
                                node
                            | _ ->
                                Node.None
        ValBinary (getNode right, BinOp operation, getNode left)
<b>
//Имплементим кастомный оператор (можно выносить во внешний модуль и не требует изменений в основной логике вызова функция для дерева, выглядите еще красивей чем на ООП -  не требует создания нового типа.
 
let divideOperator = {new IBinaryExpOp with 
                                        member this.ToStr left right = left + "/" + right
                                        member this.Eval left right  = left / right
                            }

let (/) a b = expFunBin a (ExternalBinOperator divideOperator) b
// Конец внешнего кода

let (+) a b = expFunBin a Add b

let (*) a b = expFunBin a Multiply b

let expFunUn head operation  =
        let getNode arg = match box arg with 
                            | :? int as intNode ->
                                    ValUnary (None, Lit intNode)
                            | :? Node as node -> 
                                node
                            | _ ->
                                Node.None
        ValUnary (getNode head, UnOp operation)

let (++) a = expFunUn a Increment


let resStr = toString (1 + 2 / 2 * (++)4)
let resEval = eval (1 + 2 /2 * 4 + (++)3)

Output


val resStr : string = "1 + 2/2 * ++4"
val resEval : int = 9

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

Ты предлагаешь все в одном «слое» писать или как?

Знавал таких, которые прямо с контроллера в базу лезут.

Веселее это когда логически одна сущность разорвана между 3 контролёрами и там же по кусочкам пишется в БД, а потом когда хотят добавить фичу чего-то не получается и одни баги

так я за слои — мой пример это вообще не порезанный пирог

Это не самое страшное, я видел как индусы писали свой Load Balancer при чем использовали Task (по сути класс с методом который надо выполнить в пуле потоков), при этом они его использовали как будто это Thread (если кратко то это отдельный поток), это .Net, короче есть тяп ляп, а есть шиза+LSD.

Желаю тебе как-нибудь поработать с легаси системами, которые писались без солида, дизайн паттерно и слоев, создатели уволились лет 5 назад, доков нет и система находится в состоянии «священные технологии Древних», поддержкой занимается полтора студента, которые за полгода прочитали и осознали процентов 10-20 кода чтобы понимать «что писать на вход сюда, а что — туда».

Вперед, в волшебный мир вызова базы на фронтенде и вызова фронтенда из триггеров в базе! Хорошего путешествия :)

Вперед, в волшебный мир вызова базы на фронтенде и вызова фронтенда из триггеров в базе!

щас все формошлепы так пишут

CSS ситили хардкодом в бизнес лоджик обьекте(на самом деле свалка из методов не более) на бэкэнде, про TPL и лоад бэленсер писал выше dou.ua/...​rums/topic/25805/#1470635 :-)

Lean architecture — это подход предлагаемый авторами книги www.amazon.com/...​Development/dp/0470684208

Підписатись на коментарі