Стас Выщепан @gandjustas
Умею оптимизировать программы
Information
- Rating
- 333-rd
- Location
- Москва, Москва и Московская обл., Россия
- Date of birth
- Registered
- Activity
Specialization
Software Architect, Delivery Manager
Lead
C#
.NET Core
Entity Framework
ASP.Net
Database
High-loaded systems
Designing application architecture
Git
PostgreSQL
Docker
А вот для тех кто поменьше уже очень важны визуальная привлекательность и удобство интерфейса. Иначе просто программу не будут использовать, а следовательно не будут покупать.
Особенно это ярко прослеживается на рынке мобильных приложений — частота использования приложения обратно пропорциональна времени открытия.
И зачем тебе полиморфизм?
Твой метод, который 10 секунд работает, мог быть изначально написан так, чтобы он работал 0.1 секунду, без уменьшения понятности. Просто по-другому структурируя код.
Вообще о применимости DDD давно еще писал в блоге: gandjustas.blogspot.ru/2011/12/ddd.html
ЗЫ. Проблем с дублированием запрос в linq нет, так как они прекрасно декомпозируются.
То что ты называешь «напильником» по сути является не DDD, так как не соотвествует ни одному из его принципов. Даже CQRS не является DDD, ибо нету понятия команд и событий в переметной области если ты явно туда не включишь их.
Про высокую производительность я не говорил, я говорю о достаточно производительности при мало-мальски серьезной нагрузке, когда нагружается один сервак средней руки.
Кроме того надо помнить о масштабируемости. Неоптимальные запросы, рождаемые DDD тормозят при десятках тысяч записей, просто выкидыванием DDD и небольшой оптимизацией запросов можно довести до ста тысяч с приемлимым быстродействием.
Самое важное что в таком методе мне не нужно инстанцировать класс Shipment и\или Order для получения значения. Ибо если Shipment или Order окажутся очень толстыми (агрегатами), то оно «внезапно» станет тормозить в 10 раз больше. И все ляжет при появлении серьезной нагрузки.
Вот видишь — ты сам признаешь что в любой нетривиальной системе (где есть one-to-really-many или другие сложные вещи) уже не работает DDD и надо что-то куда-то выносить.
А вот не-DDD подход, назовем его Function Driven Design (FDD), такими проблемами не страдает. Я спокойно делал обработку сотен тысяч элементов Linq запросами, просто оптимизируя индексы в базе. Для миллиона уже пришлось немного пошаманить с денормализацией и кешированием данных. И все это с сотнями одновременных пользователей.
Если ты хочешь обратится к Employee.Name по Shipment.Id, то ты пишешь запрос:
А если тебе надо табличку вывести, то ты делаешь так:
Причем такие запросы прекрасно декомпозируются, вместо ctx.Shipments ты можешь передать уже отфильтрованный набор.
Конечно без Linq сделать посложнее, но существует много других фреймворков, которые позволяют собирать запросы по кускам.
Ты просто не понимаешь как делать нормальный Data Access, у тебя мозг уже поражен DDD и ты DDDшные паттерны несознательно пытаешься воткнуть везде.
ЗЫ. Кстати частый паттерн у апологетов DDD — обвинять других в незнании DDD. Я тебя расстрою, я DDD делал еще в 10 лет назад, на делфях, когда еще и ORMов не было. И даже тогда это была не очень хорошая идея.
А тормоза возникают вот так:
Делается модель (набор классов), она отображается в БД. При выполнении операции объекты начинают обращаться к связанным объектам, которые достаются с помощью Lazy Load. Поэтому если пишешь foreach цикл по вложенным объектам у тебя получается SELECT N+1. Причем цикл написать очень просто если работает несколько человек.
Пример.
Первый программист пишет метод:
Второй программист потом пишет такой код:
Самая большая проблема когда такой код в разных слоях.
Но вот проблема найдена, но как её исправить? Обычно тут вспоминают агрегаты. Говорят что будет Order грузится сразу с Customer. А потом из-за аналогичных проблем Order должен грузиться вместе с OrderLines.
А дальше банальный вывод списка 100 ордеров начинает тянуть мегабайт данных. При мало-мальской нагрузке падает все.
Далее, если есть голова на плечах, люди просто сносят DDD, делают anemic модель и управляют загрузкой данных. А вот если головы нет, то начинается политики оптимизации Hibernate, подсовываемые через AOP, CQRS, NoSQL, гигабайтные распределенные кеши и прочая гадость.
Для того чтобы не было таких проблем изначально надо соблюдать простое правило: бизнес логика должна явно управлять загрузкой (кешированием) данных и тянуть как можно меньше данных из базы. Это, к сожалению многих «архитекторов», ломает всю идею DDD.
Проводка описывает движение объектов учета и, возможно, денег. Она не описывает состояние. Состояние описывает «остаток», дельту состояния — «оборот». И то и другое вычисляется по проводкам,
Проводка так и называется потому что соответствует некоторой операции проведения документа. Операция проведения документа вполне может зависеть от состояния системы, которое по сути «остаток».
Это и есть математическая модель учета.
Вроде же написано в той статье на которую я давал ссылку.
Вывод данных случается 95% времени и занимает 70% кода приложения. Это самая важная часть логики приложения, потому что при плохом выводе приложением невозможно пользоваться. Я не говорю об отчетах, для отчетов обычно внешний движок, я говорю об отображении данных в самом приложении. При этом получение списка сущностей может не относится к отображению данных.
Я прекрасно понимаю что для оптимизации DDD надо выкинуть, что я неоднократно делал во многих проектах. Это как раз не в пользу DDD аргумент ;)
Правда я так и не понял что за операции, которые не сводятся к просто суммированию, я таких в природе не встречал.
Пример на хабре есть: habrahabr.ru/post/66920/
Кстати тоже самое касается архитектуры вообще. Пока данных мало — встроенные средства БД помогают. Когда превышает критический порог — нужно использовать специализированные инструменты для пережевывания большого массива данных.
ЗЫ. Строить отчеты по документам, особенно в 1С — жесть, врагу не пожелаешь.
Причем описанные там методы опять таки пришли из реального мира и являются прямыми следствиями «двойной записи», только сделано все образцово-показательно.
Кстати «двойная запись» подходит вообще для любого количественного учета, а не только для бухгалтерии. 1С это убедительно доказывает.
Поймите же, нету мегапаттернов в 1С. Есть нормальная математическая модель.
Что касается CQRS, то у него нет адекватной модели, в отличие от REST например.
Никакого rocket science нет и сложная архитектура не нужна.
Апологеты DDD возлагают всю часть, связанную с отображением данных, на CQRS. То есть агрегаты не пишут в сущности, а дают «события» из которых потом происходит сборка объектов для отображения. При этом сами агрегаты начинают тормозить еще сильнее (так как требуется их сборка), но в среднем только 5% времени происходит изменение данных и 95% отображение, поэтому увеличенные тормоза не видны.
А особо крутые DDDисты научились вообще не загружать данные агрегатов, тогда вся тяжесть ложится на механизм обработки команд, начинаются пляски с распараллеливанием, потом с упорядочиванием обработки событий итд.
В итоге вместо прямой записи в базу ставится очереди, несколько воркеров, работают тяжелые механизмы синхронизации, все это покрыто весьма запутанным кодом с нечетким разделением на слои.
Тогда люди придумали вести двойную запись, но раз в период (день, неделя, месяц) подбивать остатки и обороты. На бумаге это делать несложно, а время экономит значительно.
В 1C эту модель перенесли почти один-в-один.
Еще раз повторюсь, это все не из-за «паттернов», а из-за низлежащей математической модели.
Кстати в современных СУБД, c механизмами материализованных индексов, делать явный пересчет остатков и оборотов нет смысла. Можно все сделать индексом и работать это будет в разы быстрее.