Sunday, January 4, 2009

Domain Driven Design: введение

Пару месяцев назад мне в руки попала книжка по Domain Driven Design (DDD). Отдельную рецензию на нее я напишу позже, когда дочитаю до конца. А пока что хотелось бы поделиться мыслями, которые у меня возникли в процессе прочтения.

Для начала я бы сказал, что DDD переворачивает взгляд на архитектуру приложения. Слабее, чем это делает MVC, но все же. Как нас учили проектировать в университетах, какие были наши первые проекты в реальной жизни, как программируют в 80-90% случаев в программистких конторах? Очень просто и прямолинейно. Есть у нас требования, анализируем предметную область (domain), находим сущности, анилизируем их поведение, действия, создаем высокоуровневую архитектуру, выбираем технологии, если нужно, строим UML-диаграммы. А дальше проектируем базу данных и от нее уже пляшем: слой доступа к данных (data access layer), слой бизнес-логики (business logic layer), слой представления (presentation layer), сервисы, etc. Т.е. снизу вверх. Все создаваемые таблицы в базе данных, как правило, мапятся на объекты предметной области как один-к-одному (с небольшими исключениями). Далее создаются сущности приложения, которые, как правило, подчиняются структуре сущностей в базе данных для удобства дальнейшей работы (опять же, могут быть исключения). Даже если мы используем какой-нибудь ORM (тот же Entity Framework или NHibernate), но генерируем сущности по базе данных, то мы все равно выходим на т.н. Data Driven Design, когда структура данных диктует нашу объектную модель. Да, это работает для решения многих задач, но не всех.

Domain Driven Design - совершенно другой зверь. Как следует из названия, это просто еще один способ проектирования, который фокусируется прежде всего на предметной области, на объектах реального мира, их поведении и взаимодействии, то есть фокусируется на модели и бизнес-логике, а не на структуре данных. Результатом подобного смещения приоритетов становится то, что мы стараемся перевести объекты предметной области и их поведение сразу в сущности приложения, пытаясь передать в них не только свойства и атрибуты этих объектов, но и поведение. Например, сущность "автомобиль" в DDD будет обладать не только атрибутами марка, цвет, цена, скорость и др., но и, по-возможности, каким-то поведением, связями с другими объектами. При этом проблема сохраняемости этих объектов куда-либо (например, в базу данных) отходит на второй план. Мы об этом не думаем, таким образом достигая сразу нескольких целей:

  1. Откладывание реализации слоя сохранения (persistence layer или data access layer) позволяет реализовать его тогда, когда доменная модель уже спроектирована и реализована, то есть когда она устаканилась. Сколько раз в начале разработки приложения вы добавляете или убираете колонки в таблицах или даже целые таблицы? Сколько при этом приходится переписывать кода слоя доступа к данным? Хорошо еще, если вы используете ORM.
  2. Слой сохранения реализуется как всего лишь один из инфраструктурных инструментов, а не как жизненно важный слой приложения. Акцент смещается на модель, а не на базу данных (или что у вас там) и слой доступа к ней. Это дает намного более сильный уровень независимости ваших классов от слоя сохранения (т.н. persistence ignorance), который дает возможность быстро поменять базу данных или перейти с нее на XML или вообще какие-нибудь внешние сервисы. По сути, это снижение связанности (decoupling).
  3. Persistence ignorance дает еще одну полезную возможность. За счет того, что ваш код может жить и без слоя сохранения, его можно намного проще протестировать. Более того, в начале разработки вообще можно забить как на слой сохранения, так и на слой представления и написать сначала модель (по сути, слой бизнес логики), которую покрыть модульными тестами. Причем тесты здесь выполняют уже не столько роль парашюта на будущее, сколько роль клиентского кода более верхнего или того же слоя, при помощи которого вы можете отточить API классов модели, а также быстро поправить дизайн классов при необходимости. Слой же сохранения в таком случае можно для начала просто закрыть мок-объектами. Если пойти дальше, то в случае необходимости можно написать даже слой представления над моделью, еще даже не начав писать слой сохранения.
  4. Из предыдущего пункта вытекает то, что если вы апологет TDD, то DDD - это точно ваше. Более подходящий для TDD способ проектирования и разработки приложений сложно себе представить.

Кроме всего прочего, Domain Driven Design стимулирует проектировать Rich Domain Model, то есть сущности, которые обладают не только состоянием, но и поведением. В результате разработки посредством Data Driven Design, мы очень часто получаем приложение, разработанное с использованием т.н. анемичной модели (Anemic Domain Model), в которой наши "бизнес" объекты на самом деле являются всего лишь сущностями для передачи данных из базы данных клиенту (UI, сервисы) и назад, т.е. обладают состоянием, но не поведением. Причем получаем мы это не потому что по-другому в Data Driven Design нельзя, а просто потому что в голове у нас это все те же записи из базы данных, просто переведенные на уровень кода. И как записи в базе данных не обладают никаким поведением, так и наши сущности, полученные оттуда, тоже сложно научить этому постфактум. Можно долго спорить о том, хороша ли анемичная модель или нет - это глупо. Просто как и у любого другого инструмента или подхода, у этого также есть свои положительные и отрицательные стороны и своя сфера применения. В приложениях, ориентированных лишь на CRUD операции, анемичная модель работает отлично. В приложениях, где много бизнес логики - уже не так хорошо. Бизнес логику приходится выносить в отдельные классы, которые будут оперировать объектами, хотя очень часто она по сути своей она принадлежит объектам.

Однако это еще не все. DDD - это не просто какая-то очередная методология разработки или догма проектирования. Скорее, это просто набор принципов, шаблонов, на которые можно опираться при проектировании. DDD очень активно оперирует ООП, паттернами, принципами (SOLID и пр.). Это вектор, направляющий разработку в несколько иное русло и помогающий решать задачи по-другому.

А что вы используете при проектировании приложений?

Дополнительно почитать о DDD можно в умных книжках:

  1. Eric Evans. Domain-Driven Design: Tackling Complexity in the Heart of Software
  2. Jimmy Nilsson. Applying Domain-Driven Design and Patterns: With Examples in C# and .NET
  3. Tim McCarthy. .NET Domain-Driven Design with C#: Problem - Design - Solution (Programmer to Programmer)

И в онлайне:

  1. http://domaindrivendesign.org/
  2. http://hinchcliffe.org/archive/2005/03/20/189.aspx
  3. http://www.emxsoftware.com/Domain+Driven+Design/
  4. http://www.infoq.com/articles/ddd-in-practice
  5. http://www.udidahan.com/2007/03/06/better-domain-driven-design-implementation/

44 comments:

  1. Знаешь, Шурик, все это, конечно, очень хорошо, но все-таки сильно похоже на 'yet another silver bullet'. Свое применение оно, конечно же, найдет. В конце концов нужно же как-то "переваривать" появление Data Services, Entity Framework иже с ними - вот и начинают говорить о всяком "поведении" (теже самые методы, о которых мы знаем уже давно - не так ли?) и абстагировании от хранилища (но сохранять состояние то все-равно где-то нужно - добавляем еще один слой и уровень сложности).

    Лично мне никто и раньше не мешал создавать слой бизнес логики без выноса логики в отдельные классы. При этом, конечно, в части случаев приходилось "ходить в БД" для проверки выполнения каких-то условий, но думаю ты врядли будешь спорить с тем, что чаще всего бизнес-логики действует на множестве всех объектов системы - поэтому без задействования "хранилище состояний" так или иначе не обойтись. Особой проблемы до недавних пор в этом не было - нечасто приходится кардинально менять подсистему хранения данных (и переходить с "чистого XML" к СУБД (и/или наоборот)) при сколь-нибудь существенных объемах (да и толку в этом обычно немного). Но вот MS с Azure и Data Services внесла "свежую струю" и теперь у нас есть расчудесное хранилище объектов (все фанаты DDD кричат "ура"). Вопрос только в том, сколько это будет стоить (в смысле производительности и ресурсоемкости). Особенно в случае разработки решения, способного работать и в облаке и по-старинке. С одной строные, память - это уже вроде бы и не ресурс. А с другой - электричество начали экономить даже в дата-центрах. Так что я думаю мы имеем шансы увидеть эпоху серьезной оптимизации ресурсоемкости приложений - и вот тут этот наш переслоеный пирог оказывается не так уж и вкусен. Или я ошибаюсь?

    ReplyDelete
  2. А еще я тут подумал, что неплохо было бы дожить до того дня, когда удастся абстрагироваться не только от хранилища, но и от пользователя, а потом еще и от самого программирования ;-)

    ReplyDelete
  3. AlexS Да, по поводу серебряной пули я с тобой согласен. То есть, согласен с тем, что кто-то может действительно это все воспринять как оную самую :) Конечно, это не серебряная пуля, более того судя по популярности, DDD становится еще одним buzzword. Вот я и пытался разобраться в том, есть в нем твердая основа или нет. Для меня это просто шанс взглянуть на то, как люди делают то же, что и ты, но по-другому.

    Могу сказать, что в целом опыт полезный. Мне еще не совсем понятно, с какой стороны к нему правильно подступиться на реальном проекте, но какие-то моменты можно уже пробовать. Мое мнение таково, что через какое-то время DDD в паре с TDD завоюет определенную популярность. Не знаю, будет ли этот переход похож на переход от процедурного программирования к объектно-ориентированному, но определенные параллели есть. Сейчас идет накопление количества: TDD, DDD, DSL, теория рефакторинга, agile-подходы. Вопрос лишь только, когда произойдет переход в качество и каким будет это самое качество в результате.

    > ... и абстагировании от хранилища (но сохранять состояние то все-равно где-то нужно - добавляем еще один слой и уровень сложности).

    Не совсем. Этот слой был и раньше. Просто раньше он назывался Data Access layer, а теперь - Persistence layer :) Однако разница не только в названии. При проектировании снизу вверх мы часто получаем на уровне DAL просто набор сервисных классов, реализующих CRUD-операции. Дешево, сердито, и никакого ООП. В DDD слой доступа к базе реализуется после модели и с использованием хотя бы каких-то стандартных паттернов проектирования, вроде Repository, Unit Of Work, ActiveRecord. Никто не мешает их использовать и без DDD, но как и в случае с TDD и модульными тестами, здесь их использование становится более очевидным и теоретически обоснованным для программиста. Есть какая-то база, от которой каждый может отталкиваться.

    > Лично мне никто и раньше не мешал создавать слой бизнес логики без выноса логики в отдельные классы.

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

    > Но вот MS с Azure и Data Services внесла "свежую струю" и теперь у нас есть расчудесное хранилище объектов (все фанаты DDD кричат "ура")

    Да, насчет перехода с базы данных на XML пример неубедителен. Но вот с сервисами что-то может и получиться. Насчет Azure не скажу, а вот SOA уже давно работает. Что мешает нам переехать базу на другой сервер, поставить на нее сверху Data Services или просто WCF-сервисы и работать с ней через этот прокси? Я уже видел несколько подобных архитектур, когда стояли требования по очень серьезной масштабируемости и производительности приложения, и приложение писалось сходу с ориентацией на несколько серверов back end и несколько front end.

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

    Не, ну слоев же количество не увеличивается :) Как раз DDD в этом плане ничего нового и не вносит. Просто меняет фокус. Если же ты про Azure - согласен, но фиг с ним, пока до него дойдет - рак свистнет. Тут и без него есть примеры применения, которые я описал выше.

    > А еще я тут подумал, что неплохо было бы дожить до того дня, когда удастся абстрагироваться не только от хранилища, но и от пользователя, а потом еще и от самого программирования ;-)

    Однозначно ;) На некоторых проектах иногда хочется абстрагироваться не только от пользователя, но еще от клиента, менеджмента и работы вообще :)

    ReplyDelete
  4. Александр, здравствуйте.
    Прежде всего хочу отметить, что заметка очень хорошая.
    Вообще стоит заметить, что проектирование ориентированное на модель предметной области совсем не новая методика(мне показалось, что вы рассуждаете о ней как о новой методике) и именно ей и учат в университетах(по крайней мере в том, в котором я учился и преподавал), именно ее давно используют многие софтверные компании(по крайней мере java-подразделения). У большинства коллег есть мнение в .NET менее развит данный подход потому что Microsoft пропагандирует Data Driven Design, я с этим категорически не согласен. Все дело в том, что, как это не парадаксально, Data Driven Design более понятен менее опытному разработчику, чем Domain Driven Design(это же относится к выставлению акцентов на структурах архитектуры: http://jeffreypalermo.com/blog/the-onion-architecture-part-1/). Все дело в опытности, когда приходит понимание почему использование Domain Driven Design предподчительнее и в приложениях какого уровня, разработчика можно назвать зрелым разработчиком бизнес-приложений. Мне к сожалению, в проектах по .NET направлению такие люди встречались с соотношением 1 к 10, и каждый второй из этой единицы встреченных использовал анемичную модель(эта ошибка наверное обязательное условие для последующего правильного использования шаблона Domain Model).

    ReplyDelete
  5. 'yet another silver bullet'у уже фик знает скока лет....

    А вообще именно конкретно у ДДД достаточно узкая ниша. Причем ниша в которой слишком дешевая рабочая сила, и слишком дешевые конкуренты. Это я про ентрепрайз системы. ДДД конкурирует с 1С предприятием, с ДатаСетами, со всем что описано в милионах книг типа «Система расчета ЗП за 21 минуту»… Причем достоинства ДДД в этой нише на 98% не актуальны, тут и поддержка кода и доменный язык и тестируемость..
    Что я вынес из ДДД
    1) Кучу замечательных паттернов
    2) Кучу замечательных техник
    3) Переосмысление ценностей…
    Мое ИМХО это «не-зря-потраченное-время», но в реальной жизни не юзабельное, так как в реальной жизни я еще не видел команды в которой была бы критическая масса умных людей готовых инвестировать/поверить в ценности ДДД.

    ReplyDelete
  6. >В DDD слой доступа к базе реализуется после модели и с использованием хотя бы каких-то стандартных паттернов проектирования, вроде Repository, Unit Of Work, ActiveRecord.
    ActiveRecord – не используется, по крайней мере я не вижу необходимости
    Repository – это не слой доступа к данным, это абстракция хранилища в слое модели, в общем случае обращается к инфраструктуре, а там уже ДАЛ. Хотя у Репозитария есть несколько определений.
    Unit Of Work – он в сервисах или инфраструктуре если необходима сложная операция.

    ReplyDelete
  7. Никита, спасибо за ссылку. По поводу возраста DDD я могу ошибаться. То есть, конечно же, большинство подходов и паттернов оттуда старше меня (как, например, и MVC :)), но вроде бы как самостоятельный подход DDD не так и стар. Сколько ему? TDD около 8-9 лет, если я не ошибаюсь (опять же, я не про unit-тестирование, я про TDD, как технику). А DDD вроде бы позже возник. Ну, еще MDA был раньше, но это немного другое.

    По поводу университета я ничего не могу сказать определенно. Мне кажется, я понял, что вы имеете в виду, когда говорите про университет. Вы имеете в виду какие-нибудь обычные тестовые приложения, в которых нет слоя сохранения в принципе? Тогда да. Если же нет, то тогда могу сказать, что как минимум в ХНУРЭ все выглядит именно так: база -> логика -> UI. У нас даже описание программной реализации в курсовых и дипломах, как правило, начинается со структуры БД. Почти уверен, что в других харьковских вузах ситуация ничем не лучше. Хорошо хоть страшный курс по паттернам проектирования прочли. Так что ваши вузы явно повыше уровнем :)

    Java-программисты вообще сильнее в паттернах и подходах, чем .NET-программисты. Возможно, потому что меньше встроенных в платформу средств, упрощающих жизнь. Возможно, есть и другие причины.

    ReplyDelete
  8. Миш, честно, почти ничего не понял :) Объясни, почему у DDD узкая ниша с дешевой рабочей силой? Может, я чего-то не понял во всей этой каше? Насколько я понимаю, DDD не имеет вообще никакого отношения к датасетам и 1C. Это принцип разработки. Грубо говоря, есть data driven design, есть domain driven design. Да, у нас в аутсорсинге наверно не меньше 90% приложений - data driven (то есть CRUD с небольшими рюшечками), и проектировать их лучше и проще, конечно же, при помощи data driven design. Для этого всякие databinding'и и Dynamic Data и придуманы.

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

    > Мое ИМХО это «не-зря-потраченное-время», но в реальной жизни не юзабельное, так как в реальной жизни я еще не видел команды в которой была бы критическая масса умных людей готовых инвестировать/поверить в ценности ДДД.

    У тебя вот это не вяжется с "Причем ниша в которой слишком дешевая рабочая сила, и слишком дешевые конкуренты."

    Насчет паттернов спорить не буду. Я, собственно, имел в виду скорее не местоположение реализаций, а логическую принадлежность. Все они используются для доступа к определенному хранилищу объектов и для модификации этих объектов в этом хранилище (собственно, и используются в тех или иных ORM). И если не использовать ORM, а писать что-то свое, то лучше тоже их использовать. DDD стимулирует это процесс, т.к. в нем есть хоть какая-то теоретическая база, в отличие от обычных data driven-приложений. Разве нет?

    ReplyDelete
  9. DDD в чистом виде приминимо тока ентерпрайзу. 1С, ДатаСеты это все на чем щас этот энтерпрайз строиться, и более чем успешно строится. И глупой, дешевой, индусской рабочей силы там очень много. Проанализируй наши проекты, они свободно живут на ДатаСетах, погляди на милионы 1Сников...

    Тоесть получаеться противоречие. Относительно сложное ДДД, в нише где уже есть очень легкие технологии. Я собсно про это.

    ReplyDelete
  10. О, я понял какого ключевого слова я не сказал.

    DDD конкурирует с 1С и датасетами в узкой нише. Соотвевенно програмисты ДДД конкурируют с 1Сниками и датасетчиками.

    При всем при этом у ДДД нет видимых/обьяснимых приимуществ для бизнеса.

    ReplyDelete
  11. Миш, по-моему, мы сейчас путаем теплое с мягким. Одно дело, когда мы говорим о каких-то обычных корпоративных приложениях, которые можно построить на готовых решениях (шарепоинт, 1С и т.д.) или фреймворках. Там действует закон 80/20 и тут уже нужно постараться "угадать", входят ли твои требования в эти заветные 80% или нет. Не угадал - значит, ничего от их использования не выиграл. Это разговор отдельный и к DDD не имеет практически никакого отношения. Все диктуется выбранным решением, его архитектурой и подходами.

    Другое дело, когда мы говорим о случаях НЕ использования готовых решений. Здесь уже мы сами проектируем архитектуру, выбираем платформу и те или иные инструменты. На мой взгляд, domain driven vs. data driven работает именно здесь. И не имеет особого принципиального значения, ентерпрайз это или социальная сеть. Хотя, безусловно, мы все понимаем, что во втором случае DDD со всеми своими наворотами не факт, что нужна, но какой-то сабсет использовать можно.

    Кстати, об инструментах. Никита не соглашался с тем, что MS пропагандирует data driven. А я вот сейчас перебрал в уме последние интрументы для работы с данными, которые у них появились и склонен согласится. В последнее время ОЧЕНЬ много инструментов для data driven: Linq 2 SQL, Entity Framework, Dynamic Data, Data Services, etc. Из паттерно-ориентированных вещей только ASP.NET MVC выделяется. Что-то забыл?

    ReplyDelete
  12. > При всем при этом у ДДД нет видимых/обьяснимых приимуществ для бизнеса.

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

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

    ReplyDelete
  13. Да мы про разные вещи говорим, ты про техничискую сторону, в которой domain driven vs. data driven это разные вещи. А я про бизнес строну. В ней побарабану какую ты модель выбиреш. Ей нужен результат.

    Попробуй оправдать domain driven с токи зрения бизнеса ... У меня не получаеться.

    ReplyDelete
  14. >А я вот сейчас перебрал в уме последние интрументы для работы с данными, которые у них появились и склонен согласится. В последнее время ОЧЕНЬ много инструментов для data driven: Linq 2 SQL, Entity Framework, Dynamic Data, Data Services, etc.
    Microsoft предоставляет эти верхоуровневые инструменты учитывая уровень зрелости основной массы .NET разработчиков(если бы было иначе я боюсь даже представить в чем бы ее снова обвинили), но при этом под этими инструментами лежат очень фундаментальные технологии, позволяющие использовать очень широкий спектр других подходов(разве только Microsoft должен производить инструменты?), которые и используются многими АЛЬТернативными инструментами. Я, читая книгу от Microsoft "Analyzing Reguirements and Defining Microsoft. NET Solution Architectures", сделал вывод, что они вовсе не предлагают мне проектировать по data driven принципу, даже не смотря на наличие красивых мастеров в Visual Studio, расчитанных для презентаций фундаментальных технологий и для начинающих разработчиков, которым необходимо обучаться и при этом иметь адекватный уровень производительности.

    ReplyDelete
  15. Миш, его не нужно оправдывать. Если от него есть польза больше, чем польза от data driven, то какая клиенту разница, что ты используешь с точки зрения технологий и подходов.

    Если же ты про то, как его оправдать на фоне шарепоинта или 1С, то мы возвращаемся к 80/20. Подходит, клиент согласен - пожалуйста.

    Никита, наверно, слово "зрелость" в вашей реплике нужно брать в кавычки? :) Да, Microsoft всегда старалась упростить многие вещи, за что ей во многом честь и хвала. Я же и не против этого спектра инструментов - он просто отличный. Просто подметил тенденцию. Скорее всего, это просто следствие того, что data driven подход все же проще для начинающих и средних разработчиков. И быстрее. И сфера применения шире...

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

    ReplyDelete
  16. Сходство действительно поразительное. Где предпочти жить в панельном доме или кирпичном? ))

    ReplyDelete
  17. >предпочти
    Предпочесть.

    ReplyDelete
  18. Да уж, это даже не вопрос :) Если пойти дальше, то можно посмотреть, что сейчас у нас строят уже не панелями, а литыми прямо на месте блоками, которые потом с разных сторон долепливают кирпичами. Ну чем не кодогенерация? ;) В общем, выводы каждый для себя пусть делает сам.

    ReplyDelete
  19. Как кстати к теме заметки:
    http://tinyurl.com/7u8q54

    ReplyDelete
  20. Наверно, это не очень хорошо, что я начал изучение DDD с книги "Применение DDD и шаблонов проектирования" Нильссона, а не с Эванса. Нильссон все же показывает пример реализации, паттерны, подходы, и мало делает акцент на теории DDD. Моя заметка отсюда выглядит не введением в DDD, а введением в реализацию приложений с использованием DDD. Я описываю, чему я научился из книги, забывая об идеологии. Спасибо, что ткнули носом.

    Сегодня слушал подкаст Скотта Хенселмана с Робом Конери, может, кому будет интересно:
    http://www.hanselminutes.com/default.aspx?showID=158

    Кстати, Роб сделал серию вебкастов, посвященных ASP.NET MVC, в одной из которых он начинает использовать DDD:
    http://blog.wekeroad.com/mvc-storefront/mvcstore-part-25/

    Ушел учить матчасть...

    ReplyDelete
  21. Есть хорошая новость по книге Эрика Эванса - «Domain-Driven Design: Tackling Complexity in the Heart of Software».

    Книга в производстве и скоро выйдет на русском языке в издательстве «Вильямс». Подробности в моем блоге-издателя.

    ReplyDelete
  22. Существует Domain Driven Design Study Group. Все подробности здесь: http://study-group.net/Domain_Driven_Design_Study_Group

    ReplyDelete
  23. Спасибо, Виталий, скачал ваши подкасты - послушаю.

    ReplyDelete
  24. Важный период постройки жилища — разработка проекта. В нем, помимо совместного месторасположения комнат и этажности, указываются материалы politologa, коие станут использованы при строительстве, еще схемы инженерных сетей. На данной стадии возможно оптимально распределить бюджет, предотвратив непредвиденные затраты.

    ReplyDelete