Пару месяцев назад мне в руки попала книжка по 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 будет обладать не только атрибутами марка, цвет, цена, скорость и др., но и, по-возможности, каким-то поведением, связями с другими объектами. При этом проблема сохраняемости этих объектов куда-либо (например, в базу данных) отходит на второй план. Мы об этом не думаем, таким образом достигая сразу нескольких целей:
- Откладывание реализации слоя сохранения (persistence layer или data access layer) позволяет реализовать его тогда, когда доменная модель уже спроектирована и реализована, то есть когда она устаканилась. Сколько раз в начале разработки приложения вы добавляете или убираете колонки в таблицах или даже целые таблицы? Сколько при этом приходится переписывать кода слоя доступа к данным? Хорошо еще, если вы используете ORM.
- Слой сохранения реализуется как всего лишь один из инфраструктурных инструментов, а не как жизненно важный слой приложения. Акцент смещается на модель, а не на базу данных (или что у вас там) и слой доступа к ней. Это дает намного более сильный уровень независимости ваших классов от слоя сохранения (т.н. persistence ignorance), который дает возможность быстро поменять базу данных или перейти с нее на XML или вообще какие-нибудь внешние сервисы. По сути, это снижение связанности (decoupling).
- Persistence ignorance дает еще одну полезную возможность. За счет того, что ваш код может жить и без слоя сохранения, его можно намного проще протестировать. Более того, в начале разработки вообще можно забить как на слой сохранения, так и на слой представления и написать сначала модель (по сути, слой бизнес логики), которую покрыть модульными тестами. Причем тесты здесь выполняют уже не столько роль парашюта на будущее, сколько роль клиентского кода более верхнего или того же слоя, при помощи которого вы можете отточить API классов модели, а также быстро поправить дизайн классов при необходимости. Слой же сохранения в таком случае можно для начала просто закрыть мок-объектами. Если пойти дальше, то в случае необходимости можно написать даже слой представления над моделью, еще даже не начав писать слой сохранения.
- Из предыдущего пункта вытекает то, что если вы апологет TDD, то DDD - это точно ваше. Более подходящий для TDD способ проектирования и разработки приложений сложно себе представить.
Кроме всего прочего, Domain Driven Design стимулирует проектировать Rich Domain Model, то есть сущности, которые обладают не только состоянием, но и поведением. В результате разработки посредством Data Driven Design, мы очень часто получаем приложение, разработанное с использованием т.н. анемичной модели (Anemic Domain Model), в которой наши "бизнес" объекты на самом деле являются всего лишь сущностями для передачи данных из базы данных клиенту (UI, сервисы) и назад, т.е. обладают состоянием, но не поведением. Причем получаем мы это не потому что по-другому в Data Driven Design нельзя, а просто потому что в голове у нас это все те же записи из базы данных, просто переведенные на уровень кода. И как записи в базе данных не обладают никаким поведением, так и наши сущности, полученные оттуда, тоже сложно научить этому постфактум. Можно долго спорить о том, хороша ли анемичная модель или нет - это глупо. Просто как и у любого другого инструмента или подхода, у этого также есть свои положительные и отрицательные стороны и своя сфера применения. В приложениях, ориентированных лишь на CRUD операции, анемичная модель работает отлично. В приложениях, где много бизнес логики - уже не так хорошо. Бизнес логику приходится выносить в отдельные классы, которые будут оперировать объектами, хотя очень часто она по сути своей она принадлежит объектам.
Однако это еще не все. DDD - это не просто какая-то очередная методология разработки или догма проектирования. Скорее, это просто набор принципов, шаблонов, на которые можно опираться при проектировании. DDD очень активно оперирует ООП, паттернами, принципами (SOLID и пр.). Это вектор, направляющий разработку в несколько иное русло и помогающий решать задачи по-другому.
А что вы используете при проектировании приложений?
Дополнительно почитать о DDD можно в умных книжках:
- Eric Evans. Domain-Driven Design: Tackling Complexity in the Heart of Software
- Jimmy Nilsson. Applying Domain-Driven Design and Patterns: With Examples in C# and .NET
- Tim McCarthy. .NET Domain-Driven Design with C#: Problem - Design - Solution (Programmer to Programmer)
И в онлайне:
Знаешь, Шурик, все это, конечно, очень хорошо, но все-таки сильно похоже на 'yet another silver bullet'. Свое применение оно, конечно же, найдет. В конце концов нужно же как-то "переваривать" появление Data Services, Entity Framework иже с ними - вот и начинают говорить о всяком "поведении" (теже самые методы, о которых мы знаем уже давно - не так ли?) и абстагировании от хранилища (но сохранять состояние то все-равно где-то нужно - добавляем еще один слой и уровень сложности).
ReplyDeleteЛично мне никто и раньше не мешал создавать слой бизнес логики без выноса логики в отдельные классы. При этом, конечно, в части случаев приходилось "ходить в БД" для проверки выполнения каких-то условий, но думаю ты врядли будешь спорить с тем, что чаще всего бизнес-логики действует на множестве всех объектов системы - поэтому без задействования "хранилище состояний" так или иначе не обойтись. Особой проблемы до недавних пор в этом не было - нечасто приходится кардинально менять подсистему хранения данных (и переходить с "чистого XML" к СУБД (и/или наоборот)) при сколь-нибудь существенных объемах (да и толку в этом обычно немного). Но вот MS с Azure и Data Services внесла "свежую струю" и теперь у нас есть расчудесное хранилище объектов (все фанаты DDD кричат "ура"). Вопрос только в том, сколько это будет стоить (в смысле производительности и ресурсоемкости). Особенно в случае разработки решения, способного работать и в облаке и по-старинке. С одной строные, память - это уже вроде бы и не ресурс. А с другой - электричество начали экономить даже в дата-центрах. Так что я думаю мы имеем шансы увидеть эпоху серьезной оптимизации ресурсоемкости приложений - и вот тут этот наш переслоеный пирог оказывается не так уж и вкусен. Или я ошибаюсь?
А еще я тут подумал, что неплохо было бы дожить до того дня, когда удастся абстрагироваться не только от хранилища, но и от пользователя, а потом еще и от самого программирования ;-)
ReplyDeleteAlexS Да, по поводу серебряной пули я с тобой согласен. То есть, согласен с тем, что кто-то может действительно это все воспринять как оную самую :) Конечно, это не серебряная пуля, более того судя по популярности, DDD становится еще одним buzzword. Вот я и пытался разобраться в том, есть в нем твердая основа или нет. Для меня это просто шанс взглянуть на то, как люди делают то же, что и ты, но по-другому.
ReplyDeleteМогу сказать, что в целом опыт полезный. Мне еще не совсем понятно, с какой стороны к нему правильно подступиться на реальном проекте, но какие-то моменты можно уже пробовать. Мое мнение таково, что через какое-то время 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Прежде всего хочу отметить, что заметка очень хорошая.
Вообще стоит заметить, что проектирование ориентированное на модель предметной области совсем не новая методика(мне показалось, что вы рассуждаете о ней как о новой методике) и именно ей и учат в университетах(по крайней мере в том, в котором я учился и преподавал), именно ее давно используют многие софтверные компании(по крайней мере 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).
'yet another silver bullet'у уже фик знает скока лет....
ReplyDeleteА вообще именно конкретно у ДДД достаточно узкая ниша. Причем ниша в которой слишком дешевая рабочая сила, и слишком дешевые конкуренты. Это я про ентрепрайз системы. ДДД конкурирует с 1С предприятием, с ДатаСетами, со всем что описано в милионах книг типа «Система расчета ЗП за 21 минуту»… Причем достоинства ДДД в этой нише на 98% не актуальны, тут и поддержка кода и доменный язык и тестируемость..
Что я вынес из ДДД
1) Кучу замечательных паттернов
2) Кучу замечательных техник
3) Переосмысление ценностей…
Мое ИМХО это «не-зря-потраченное-время», но в реальной жизни не юзабельное, так как в реальной жизни я еще не видел команды в которой была бы критическая масса умных людей готовых инвестировать/поверить в ценности ДДД.
>В DDD слой доступа к базе реализуется после модели и с использованием хотя бы каких-то стандартных паттернов проектирования, вроде Repository, Unit Of Work, ActiveRecord.
ReplyDeleteActiveRecord – не используется, по крайней мере я не вижу необходимости
Repository – это не слой доступа к данным, это абстракция хранилища в слое модели, в общем случае обращается к инфраструктуре, а там уже ДАЛ. Хотя у Репозитария есть несколько определений.
Unit Of Work – он в сервисах или инфраструктуре если необходима сложная операция.
Никита, спасибо за ссылку. По поводу возраста DDD я могу ошибаться. То есть, конечно же, большинство подходов и паттернов оттуда старше меня (как, например, и MVC :)), но вроде бы как самостоятельный подход DDD не так и стар. Сколько ему? TDD около 8-9 лет, если я не ошибаюсь (опять же, я не про unit-тестирование, я про TDD, как технику). А DDD вроде бы позже возник. Ну, еще MDA был раньше, но это немного другое.
ReplyDeleteПо поводу университета я ничего не могу сказать определенно. Мне кажется, я понял, что вы имеете в виду, когда говорите про университет. Вы имеете в виду какие-нибудь обычные тестовые приложения, в которых нет слоя сохранения в принципе? Тогда да. Если же нет, то тогда могу сказать, что как минимум в ХНУРЭ все выглядит именно так: база -> логика -> UI. У нас даже описание программной реализации в курсовых и дипломах, как правило, начинается со структуры БД. Почти уверен, что в других харьковских вузах ситуация ничем не лучше. Хорошо хоть страшный курс по паттернам проектирования прочли. Так что ваши вузы явно повыше уровнем :)
Java-программисты вообще сильнее в паттернах и подходах, чем .NET-программисты. Возможно, потому что меньше встроенных в платформу средств, упрощающих жизнь. Возможно, есть и другие причины.
Миш, честно, почти ничего не понял :) Объясни, почему у DDD узкая ниша с дешевой рабочей силой? Может, я чего-то не понял во всей этой каше? Насколько я понимаю, DDD не имеет вообще никакого отношения к датасетам и 1C. Это принцип разработки. Грубо говоря, есть data driven design, есть domain driven design. Да, у нас в аутсорсинге наверно не меньше 90% приложений - data driven (то есть CRUD с небольшими рюшечками), и проектировать их лучше и проще, конечно же, при помощи data driven design. Для этого всякие databinding'и и Dynamic Data и придуманы.
ReplyDeleteПо поводу того, что выносишь из DDD кучу полезных знаний и новое мировосприятие - с этим однозначно согласен.
> Мое ИМХО это «не-зря-потраченное-время», но в реальной жизни не юзабельное, так как в реальной жизни я еще не видел команды в которой была бы критическая масса умных людей готовых инвестировать/поверить в ценности ДДД.
У тебя вот это не вяжется с "Причем ниша в которой слишком дешевая рабочая сила, и слишком дешевые конкуренты."
Насчет паттернов спорить не буду. Я, собственно, имел в виду скорее не местоположение реализаций, а логическую принадлежность. Все они используются для доступа к определенному хранилищу объектов и для модификации этих объектов в этом хранилище (собственно, и используются в тех или иных ORM). И если не использовать ORM, а писать что-то свое, то лучше тоже их использовать. DDD стимулирует это процесс, т.к. в нем есть хоть какая-то теоретическая база, в отличие от обычных data driven-приложений. Разве нет?
DDD в чистом виде приминимо тока ентерпрайзу. 1С, ДатаСеты это все на чем щас этот энтерпрайз строиться, и более чем успешно строится. И глупой, дешевой, индусской рабочей силы там очень много. Проанализируй наши проекты, они свободно живут на ДатаСетах, погляди на милионы 1Сников...
ReplyDeleteТоесть получаеться противоречие. Относительно сложное ДДД, в нише где уже есть очень легкие технологии. Я собсно про это.
О, я понял какого ключевого слова я не сказал.
ReplyDeleteDDD конкурирует с 1С и датасетами в узкой нише. Соотвевенно програмисты ДДД конкурируют с 1Сниками и датасетчиками.
При всем при этом у ДДД нет видимых/обьяснимых приимуществ для бизнеса.
Миш, по-моему, мы сейчас путаем теплое с мягким. Одно дело, когда мы говорим о каких-то обычных корпоративных приложениях, которые можно построить на готовых решениях (шарепоинт, 1С и т.д.) или фреймворках. Там действует закон 80/20 и тут уже нужно постараться "угадать", входят ли твои требования в эти заветные 80% или нет. Не угадал - значит, ничего от их использования не выиграл. Это разговор отдельный и к DDD не имеет практически никакого отношения. Все диктуется выбранным решением, его архитектурой и подходами.
ReplyDeleteДругое дело, когда мы говорим о случаях НЕ использования готовых решений. Здесь уже мы сами проектируем архитектуру, выбираем платформу и те или иные инструменты. На мой взгляд, 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На мой взгляд, все же есть. Для больших, длительных, масштабируемых приложений. Но лучше не буду ничего говорить пока не попробую DDD на практике.
Как ты сам сказал, самое главное в DDD - это то, что он дает свежий поток мыслей и приносит знания по проектированию и архитектуре.
Да мы про разные вещи говорим, ты про техничискую сторону, в которой domain driven vs. data driven это разные вещи. А я про бизнес строну. В ней побарабану какую ты модель выбиреш. Ей нужен результат.
ReplyDeleteПопробуй оправдать domain driven с токи зрения бизнеса ... У меня не получаеться.
>А я вот сейчас перебрал в уме последние интрументы для работы с данными, которые у них появились и склонен согласится. В последнее время ОЧЕНЬ много инструментов для data driven: Linq 2 SQL, Entity Framework, Dynamic Data, Data Services, etc.
ReplyDeleteMicrosoft предоставляет эти верхоуровневые инструменты учитывая уровень зрелости основной массы .NET разработчиков(если бы было иначе я боюсь даже представить в чем бы ее снова обвинили), но при этом под этими инструментами лежат очень фундаментальные технологии, позволяющие использовать очень широкий спектр других подходов(разве только Microsoft должен производить инструменты?), которые и используются многими АЛЬТернативными инструментами. Я, читая книгу от Microsoft "Analyzing Reguirements and Defining Microsoft. NET Solution Architectures", сделал вывод, что они вовсе не предлагают мне проектировать по data driven принципу, даже не смотря на наличие красивых мастеров в Visual Studio, расчитанных для презентаций фундаментальных технологий и для начинающих разработчиков, которым необходимо обучаться и при этом иметь адекватный уровень производительности.
Миш, его не нужно оправдывать. Если от него есть польза больше, чем польза от data driven, то какая клиенту разница, что ты используешь с точки зрения технологий и подходов.
ReplyDeleteЕсли же ты про то, как его оправдать на фоне шарепоинта или 1С, то мы возвращаемся к 80/20. Подходит, клиент согласен - пожалуйста.
Никита, наверно, слово "зрелость" в вашей реплике нужно брать в кавычки? :) Да, Microsoft всегда старалась упростить многие вещи, за что ей во многом честь и хвала. Я же и не против этого спектра инструментов - он просто отличный. Просто подметил тенденцию. Скорее всего, это просто следствие того, что data driven подход все же проще для начинающих и средних разработчиков. И быстрее. И сфера применения шире...
Тогда возникает вопрос: так зачем тогда все эти сложности с паттернами, подходами, архитектурами? Или это просто эволюция? Раньше были нужны эти более крупные кирпичи, чтобы строить более серьезные знания, а сейчас технологии уже шагнули дальше и мы можем строить дом сразу из готовых бетонных блоков. Все же сильно пересекаются миры реальной и виртуальной архитектуры и строительства...
Сходство действительно поразительное. Где предпочти жить в панельном доме или кирпичном? ))
ReplyDelete>предпочти
ReplyDeleteПредпочесть.
Да уж, это даже не вопрос :) Если пойти дальше, то можно посмотреть, что сейчас у нас строят уже не панелями, а литыми прямо на месте блоками, которые потом с разных сторон долепливают кирпичами. Ну чем не кодогенерация? ;) В общем, выводы каждый для себя пусть делает сам.
ReplyDeleteКак кстати к теме заметки:
ReplyDeletehttp://tinyurl.com/7u8q54
Наверно, это не очень хорошо, что я начал изучение DDD с книги "Применение DDD и шаблонов проектирования" Нильссона, а не с Эванса. Нильссон все же показывает пример реализации, паттерны, подходы, и мало делает акцент на теории DDD. Моя заметка отсюда выглядит не введением в DDD, а введением в реализацию приложений с использованием DDD. Я описываю, чему я научился из книги, забывая об идеологии. Спасибо, что ткнули носом.
ReplyDeleteСегодня слушал подкаст Скотта Хенселмана с Робом Конери, может, кому будет интересно:
http://www.hanselminutes.com/default.aspx?showID=158
Кстати, Роб сделал серию вебкастов, посвященных ASP.NET MVC, в одной из которых он начинает использовать DDD:
http://blog.wekeroad.com/mvc-storefront/mvcstore-part-25/
Ушел учить матчасть...
Есть хорошая новость по книге Эрика Эванса - «Domain-Driven Design: Tackling Complexity in the Heart of Software».
ReplyDeleteКнига в производстве и скоро выйдет на русском языке в издательстве «Вильямс». Подробности в моем блоге-издателя.
Существует Domain Driven Design Study Group. Все подробности здесь: http://study-group.net/Domain_Driven_Design_Study_Group
ReplyDeleteСпасибо, Виталий, скачал ваши подкасты - послушаю.
ReplyDeleteВажный период постройки жилища — разработка проекта. В нем, помимо совместного месторасположения комнат и этажности, указываются материалы politologa, коие станут использованы при строительстве, еще схемы инженерных сетей. На данной стадии возможно оптимально распределить бюджет, предотвратив непредвиденные затраты.
ReplyDeleteVan
ReplyDeleteizmir
Artvin
Tunceli
Eskişehir
WF1VH6
görüntülü.show
ReplyDeletewhatsapp ücretli show
ZWUH
ankara parça eşya taşıma
ReplyDeletetakipçi satın al
antalya rent a car
antalya rent a car
ankara parça eşya taşıma
5Oİ
maraş evden eve nakliyat
ReplyDeletemalatya evden eve nakliyat
ağrı evden eve nakliyat
elazığ evden eve nakliyat
aydın evden eve nakliyat
TRFR
086F4
ReplyDeleteKilis Evden Eve Nakliyat
Düzce Lojistik
Uşak Parça Eşya Taşıma
Tokat Parça Eşya Taşıma
Iğdır Parça Eşya Taşıma
5DA32
ReplyDeletereferans
B9DEC
ReplyDeletegoruntulu sohbet
Kırıkkale Yabancı Görüntülü Sohbet Siteleri
Amasya Rastgele Sohbet
kadınlarla ücretsiz sohbet
adana en iyi sesli sohbet uygulamaları
manisa en iyi sesli sohbet uygulamaları
telefonda görüntülü sohbet
elazığ rastgele sohbet siteleri
kayseri parasız sohbet
8A56A
ReplyDeletecanlı sohbet
bilecik canlı ücretsiz sohbet
ankara random görüntülü sohbet
kocaeli telefonda rastgele sohbet
nevşehir sesli sohbet siteleri
afyon canlı sohbet ücretsiz
Kırıkkale Mobil Sesli Sohbet
nevşehir en iyi ücretsiz görüntülü sohbet siteleri
yabancı görüntülü sohbet uygulamaları
4B8E0
ReplyDeleteParasız Görüntülü Sohbet
Facebook Grup Üyesi Hilesi
Kripto Para Çıkarma Siteleri
Onlyfans Takipçi Hilesi
Binance Madenciliği Nedir
Binance Para Kazanma
Coin Nasıl Kazılır
Bitcoin Nasıl Oynanır
Aion Coin Hangi Borsada
E8063
ReplyDeleteYoutube Beğeni Satın Al
Alyattes Coin Hangi Borsada
Binance Referans Kodu
Dxy Coin Hangi Borsada
Baby Doge Coin Hangi Borsada
Kwai Beğeni Satın Al
Bitcoin Oynama
Instagram Takipçi Hilesi
Kripto Para Üretme
BC7AE
ReplyDeleteKripto Para Madenciliği Siteleri
Tumblr Takipçi Satın Al
Tiktok Beğeni Hilesi
Linkedin Beğeni Satın Al
Onlyfans Beğeni Satın Al
Bonk Coin Hangi Borsada
MEME Coin Hangi Borsada
Dxy Coin Hangi Borsada
Referans Kimliği Nedir
4F7B0
ReplyDeleteAydın İftar Saatleri
İzmir İftar Saatleri
Çorum İftar Saatleri
Bitlis İftar Saatleri
Giresun İftar Saatleri
Eskişehir İftar Saatleri
Kütahya İftar Saatleri
Kastamonu İftar Saatleri
Edirne İftar Saatleri
refegtredgbthbfhfghn
ReplyDeleteشركة مكافحة حشرات بالهفوف
dsgvrdht
ReplyDeleteصيانة افران جدة
شركة تنظيف خزانات u7IOOO7RwE
ReplyDeleteشركة عزل اسطح بالقطيف 2opfSMOZuC
ReplyDeleteشركة تسليك مجاري بالجبيل ZinAVNDITs
ReplyDeleteشركة تنظيف سجاد بالجبيل NqPO5242Zn
ReplyDeleteشركة تنظيف خزانات B3sOSM5qOU
ReplyDeleteشركة تنظيف كنب بالدمام UFxLQN9w0l
ReplyDelete