[Что такое Data Lake?]
Привет, друзья! Сегодня хочу рассказать о Data Lake, одном из ключевых компонентов в современной работе с большими данными.
Data Lake — это хранилище, которое позволяет хранить огромное количество структурированных и неструктурированных данных. В отличие от традиционных баз данных, Data Lake позволяет загружать данные в их оригинальном формате, а затем обрабатывать и анализировать их по мере необходимости.
Основные преимущества Data Lake:
Гибкость: можно хранить любые данные в любом формате.
Масштабируемость: легко справляется с увеличением объема данных.
Экономичность: хранение данных в сыром виде обходится дешевле.
Почему Data Lake так популярен:
Разнообразие данных: Современные компании работают с огромным количеством разнообразных данных, включая логи, социальные медиа, данные IoT и многое другое. Data Lake позволяет легко хранить и анализировать все эти данные в одном месте.
Гибкость анализа: Data Lake поддерживает различные аналитические инструменты и технологии, включая машинное обучение, аналитические платформы и SQL-запросы. Это позволяет аналитикам и разработчикам быстро адаптироваться к новым задачам и требованиям.
Поддержка масштабных данных: В эпоху больших данных возможность быстро масштабировать хранилище данных становится критически важной. Data Lake обеспечивает высокую масштабируемость без значительных затрат на инфраструктуру.
Быстрое внедрение инноваций: Возможность быстро интегрировать новые технологии и подходы к обработке данных делает Data Lake идеальным решением для компаний, стремящихся к инновациям.
Основные недостатки Data Lake:
Сложность управления: Без надлежащей структуры и управления Data Lake может превратиться в "болото данных" (data swamp), где трудно найти нужные данные.
Проблемы безопасности: Хранение большого объема данных в одном месте требует усиленных мер безопасности для защиты от утечек и несанкционированного доступа.
Требования к знаниям: Эффективное использование Data Lake требует знаний и опыта в управлении данными и аналитике, что может быть проблемой для компаний с ограниченными ресурсами.
Качество данных: Поскольку данные хранятся в их сыром виде, может возникнуть проблема с обеспечением их качества и консистентности.
———
В следующем посте расскажу, какие инструменты используются для управления Data Lake и как они помогают решать повседневные задачи.
#BigData #DataLake #Python #IT
Привет, друзья! Сегодня хочу рассказать о Data Lake, одном из ключевых компонентов в современной работе с большими данными.
Data Lake — это хранилище, которое позволяет хранить огромное количество структурированных и неструктурированных данных. В отличие от традиционных баз данных, Data Lake позволяет загружать данные в их оригинальном формате, а затем обрабатывать и анализировать их по мере необходимости.
Основные преимущества Data Lake:
Гибкость: можно хранить любые данные в любом формате.
Масштабируемость: легко справляется с увеличением объема данных.
Экономичность: хранение данных в сыром виде обходится дешевле.
Почему Data Lake так популярен:
Разнообразие данных: Современные компании работают с огромным количеством разнообразных данных, включая логи, социальные медиа, данные IoT и многое другое. Data Lake позволяет легко хранить и анализировать все эти данные в одном месте.
Гибкость анализа: Data Lake поддерживает различные аналитические инструменты и технологии, включая машинное обучение, аналитические платформы и SQL-запросы. Это позволяет аналитикам и разработчикам быстро адаптироваться к новым задачам и требованиям.
Поддержка масштабных данных: В эпоху больших данных возможность быстро масштабировать хранилище данных становится критически важной. Data Lake обеспечивает высокую масштабируемость без значительных затрат на инфраструктуру.
Быстрое внедрение инноваций: Возможность быстро интегрировать новые технологии и подходы к обработке данных делает Data Lake идеальным решением для компаний, стремящихся к инновациям.
Основные недостатки Data Lake:
Сложность управления: Без надлежащей структуры и управления Data Lake может превратиться в "болото данных" (data swamp), где трудно найти нужные данные.
Проблемы безопасности: Хранение большого объема данных в одном месте требует усиленных мер безопасности для защиты от утечек и несанкционированного доступа.
Требования к знаниям: Эффективное использование Data Lake требует знаний и опыта в управлении данными и аналитике, что может быть проблемой для компаний с ограниченными ресурсами.
Качество данных: Поскольку данные хранятся в их сыром виде, может возникнуть проблема с обеспечением их качества и консистентности.
———
В следующем посте расскажу, какие инструменты используются для управления Data Lake и как они помогают решать повседневные задачи.
#BigData #DataLake #Python #IT
[Важность контроля доступа к разделяемым объектам]
Привет, друзья! Сегодня хочу поделиться интересным примером из книги "Операционные системы. Основы и принципы" Х. Дейтел. Этот пример демонстрирует важность контроля доступа к разделяемым объектам в параллельных потоках. Несоблюдение этого правила может привести к непредсказуемым результатам работы программы.
Код по ссылке: https://github.com/avagners/algorithms_and_data_structures/blob/main/algorithms/asynchronous_concurrent_execution/unsynchronized_buffer.py
Этот код создает два потока: производитель (Producer) и потребитель (Consumer), которые работают с общим ресурсом - буфером (Buffer). Производитель генерирует данные и добавляет их в буфер, а потребитель извлекает данные из буфера и суммирует их.
Ключевые моменты:
Buffer: Хранит данные в виде множества (set). Методы set и get используются для добавления и извлечения данных.
Producer: В цикле генерирует числа от 1 до 4, добавляет их в буфер и выводит сообщение о записи.
Consumer: В цикле извлекает числа из буфера, суммирует их и выводит сообщение о чтении.
Проблемы многопоточности
Каждый запуск этого кода будет выдавать непредсказуемый результат. Это происходит из-за того, что доступ к буферу не синхронизирован. Потоки могут одновременно пытаться читать и записывать данные, что приводит к ошибкам и некорректным результатам.
Важно помнить:
При работе с параллельными потоками необходимо тщательно контролировать доступ к разделяемым ресурсам. Это можно сделать с помощью механизмов синхронизации, таких как блокировки (lock), семафоры и другие средства.
В следующих постах я покажу и расскажу об алгоритмах взаимоисключений.
#Python #Multithreading #Concurrency #Programming #IT
Привет, друзья! Сегодня хочу поделиться интересным примером из книги "Операционные системы. Основы и принципы" Х. Дейтел. Этот пример демонстрирует важность контроля доступа к разделяемым объектам в параллельных потоках. Несоблюдение этого правила может привести к непредсказуемым результатам работы программы.
Код по ссылке: https://github.com/avagners/algorithms_and_data_structures/blob/main/algorithms/asynchronous_concurrent_execution/unsynchronized_buffer.py
Этот код создает два потока: производитель (Producer) и потребитель (Consumer), которые работают с общим ресурсом - буфером (Buffer). Производитель генерирует данные и добавляет их в буфер, а потребитель извлекает данные из буфера и суммирует их.
Ключевые моменты:
Buffer: Хранит данные в виде множества (set). Методы set и get используются для добавления и извлечения данных.
Producer: В цикле генерирует числа от 1 до 4, добавляет их в буфер и выводит сообщение о записи.
Consumer: В цикле извлекает числа из буфера, суммирует их и выводит сообщение о чтении.
Проблемы многопоточности
Каждый запуск этого кода будет выдавать непредсказуемый результат. Это происходит из-за того, что доступ к буферу не синхронизирован. Потоки могут одновременно пытаться читать и записывать данные, что приводит к ошибкам и некорректным результатам.
Важно помнить:
При работе с параллельными потоками необходимо тщательно контролировать доступ к разделяемым ресурсам. Это можно сделать с помощью механизмов синхронизации, таких как блокировки (lock), семафоры и другие средства.
В следующих постах я покажу и расскажу об алгоритмах взаимоисключений.
#Python #Multithreading #Concurrency #Programming #IT
[Алгоритм Питерсона*: Обеспечение взаимного исключения в многопоточности]
Привет, друзья! Помните пост "Важность контроля доступа к разделяемым объектам", где была продемонстрирована проблема при работе с параллельными потоками? Сегодня расскажу об интересном алгоритме для обеспечения взаимного исключения — алгоритме Питерсона. Это простое, но важное решение, которое позволяет двум потокам безопасно разделять ресурсы.
Что такое алгоритм Питерсона?
Алгоритм Питерсона — это классический способ синхронизации двух потоков, обеспечивающий их корректное взаимодействие при совместном доступе к общим данным. Он был предложен Гарри Питерсоном в 1981 году и стал одним из первых алгоритмов, которые решили проблему взаимного исключения без использования сложных синхронизирующих механизмов.
Как это работает?
Алгоритм Питерсона позволяет двум потокам координировать доступ к общим ресурсам. В нашем примере используется класс
В этом коде два потока устанавливают свои флаги и координируют доступ к ресурсу через переменную
Зачем это нужно?
Хотя алгоритм Питерсона редко используется на практике в современных системах, он остаётся важным учебным примером, который помогает понять основные концепции синхронизации и взаимного исключения. Его простота делает его отличным инструментом для изучения основ многопоточного программирования.
Кто такой Питерсон?
Гарри Питерсон — американский учёный, который внёс значительный вклад в области информатики, особенно в разработку методов синхронизации и управления параллельными процессами. Его алгоритм стал основой для многих учебных курсов и учебников по операционным системам.
———
Далее я покажу другой алгоритм взаимного исключения для N-потоков - алгоритм Лэмпорта.
*В других русскоязычных источниках фамилию пишут через "е" - Петерсон.
Полный пример кода по ссылке: https://github.com/avagners/algorithms_and_data_structures/blob/main/algorithms/asynchronous_concurrent_execution/peterson_lock/peterson_lock_2.py
#Python #Concurrency #PetersonsAlgorithm #Многопоточность #Синхронизация #IT
Привет, друзья! Помните пост "Важность контроля доступа к разделяемым объектам", где была продемонстрирована проблема при работе с параллельными потоками? Сегодня расскажу об интересном алгоритме для обеспечения взаимного исключения — алгоритме Питерсона. Это простое, но важное решение, которое позволяет двум потокам безопасно разделять ресурсы.
Что такое алгоритм Питерсона?
Алгоритм Питерсона — это классический способ синхронизации двух потоков, обеспечивающий их корректное взаимодействие при совместном доступе к общим данным. Он был предложен Гарри Питерсоном в 1981 году и стал одним из первых алгоритмов, которые решили проблему взаимного исключения без использования сложных синхронизирующих механизмов.
Как это работает?
Алгоритм Питерсона позволяет двум потокам координировать доступ к общим ресурсам. В нашем примере используется класс
PetersonLock
, который реализует этот алгоритм. Два потока (производитель и потребитель) используют блокировки для безопасной записи и чтения данных из общего буфера.class PetersonLock:
def __init__(self):
self.flag: List[bool, bool] = [False, False]
self.favored_thread: int = 0
def lock(self, thread_id):
other_thread = 1 - thread_id
self.flag[thread_id] = True
self.favored_thread = thread_id
while self.flag[other_thread] and self.favored_thread == thread_id:
pass
def unlock(self, thread_id):
self.flag[thread_id] = False
В этом коде два потока устанавливают свои флаги и координируют доступ к ресурсу через переменную
favored_thread
, обеспечивая таким образом взаимное исключение.Зачем это нужно?
Хотя алгоритм Питерсона редко используется на практике в современных системах, он остаётся важным учебным примером, который помогает понять основные концепции синхронизации и взаимного исключения. Его простота делает его отличным инструментом для изучения основ многопоточного программирования.
Кто такой Питерсон?
Гарри Питерсон — американский учёный, который внёс значительный вклад в области информатики, особенно в разработку методов синхронизации и управления параллельными процессами. Его алгоритм стал основой для многих учебных курсов и учебников по операционным системам.
———
Далее я покажу другой алгоритм взаимного исключения для N-потоков - алгоритм Лэмпорта.
*В других русскоязычных источниках фамилию пишут через "е" - Петерсон.
Полный пример кода по ссылке: https://github.com/avagners/algorithms_and_data_structures/blob/main/algorithms/asynchronous_concurrent_execution/peterson_lock/peterson_lock_2.py
#Python #Concurrency #PetersonsAlgorithm #Многопоточность #Синхронизация #IT
[Entity и Value Object в DDD]
Привет! В DDD сразу встречаешь два ключевых понятия — Entity и Value Object. Оба вроде как про данные, но ведут себя по-разному.
Что такое `Entity`?
Что такое `Value Object`?
Когда что использовать?
- Если объект уникален сам по себе — это
- Если важны только его значения — это
Фишка DDD:
Хорошая практика — делать
#DDD #Python #Entity #ValueObject #Программирование
Привет! В DDD сразу встречаешь два ключевых понятия — Entity и Value Object. Оба вроде как про данные, но ведут себя по-разному.
Что такое `Entity`?
Entity
— это объект, который имеет уникальный идентификатор и жизненный цикл. Его идентичность важна, и она сохраняется на протяжении всего времени существования объекта. Например, пользователь в системе (User) — это Entity
, потому что у него есть уникальный ID, и даже если его имя или email изменятся, это все равно будет тот же самый пользователь.from dataclasses import dataclass, field
import uuid
@dataclass
class User:
name: str
email: str
id: uuid.UUID = field(default_factory=uuid.uuid4, init=False)
def __eq__(self, other):
if isinstance(other, User):
return self.id == other.id
return False
# Пример использования:
user1 = User("Иван", "ivan@example.com")
user2 = User("Иван", "ivan@example.com")
print(user1) # User(name='Иван', email='ivan@example.com', id=...)
print(user2) # User(name='Иван', email='ivan@example.com', id=...)
print(user1 == user2) # False, потому что ID разные
Что такое `Value Object`?
Value Object
, в отличие от Entity
, не имеет идентичности. Он определяется своими атрибутами, и если все атрибуты одинаковы, то два Value Object
считаются равными. Value Object
обычно неизменяемы (immutable), что делает их более предсказуемыми и безопасными в использовании.from dataclasses import dataclass
import re
@dataclass(frozen=True)
class EmailAddress:
value: str
def __post_init__(self):
if not self._is_valid_email(self.value):
raise ValueError(f"Некорректный email: {self.value}")
@staticmethod
def _is_valid_email(email: str) -> bool:
pattern = r"^[\w\.-]+@[\w\.-]+\.\w+$"
return re.match(pattern, email) is not None
# Пример использования:
try:
email1 = EmailAddress("valid.email@example.com")
print(email1) # EmailAddress(value='valid.email@example.com')
email2 = EmailAddress("invalid-email") # Ошибка валидации!
except ValueError as e:
print(e)
Когда что использовать?
- Если объект уникален сам по себе — это
Entity
.- Если важны только его значения — это
Value Object
.Фишка DDD:
Хорошая практика — делать
Value Objects
иммутабельными и использовать их для инкапсуляции логики. Например, не просто string
для email
, а EmailAddress
, который сразу валидирует значение.Entity
и Value Object
— это мощные концепции, которые помогают структурировать код и делать его более предсказуемым. В Python их реализация может быть элегантной с использованием dataclasses
.#DDD #Python #Entity #ValueObject #Программирование
[Агрегаты в DDD: Организация сложности через границы]
В Domain-Driven Design (DDD) работа с сложными доменными моделями требует четкой структуры, чтобы избежать хаоса.
Агрегат (Aggregate) — один из ключевых паттернов DDD, который помогает управлять целостностью данных и упрощает взаимодействие с моделью.
Что такое Aggregate?
Агрегат — это кластер связанных объектов, рассматриваемых как единое целое.
Он объединяет:
1) Корень агрегата (Aggregate Root) — единственная точка входа для внешних взаимодействий.
2) Внутренние сущности (Entity) и value-объекты (ValueObject) — элементы, которые могут изменяться только через корень.
По своей сути агрегат - это тоже Entity.
Зачем нужны агрегаты?
- Консистентность данных
Агрегат гарантирует, что изменения внутри него соответствуют бизнес-правилам. Например, нельзя добавить OrderItem с отрицательной ценой — корень проверяет это.
- Границы транзакций
Изменения в рамках одного агрегата обычно выполняются в одной транзакции. Это упрощает управление конкурентным доступом.
- Сокрытие сложности
Внешние системы не знают о внутренней структуре агрегата — они работают только с корнем.
Ключевые принципы проектирования:
- Единый корень
Все внешние запросы идут через корень. Если нужно изменить дочерний объект — делайте это через методы корня.
- Инварианты внутри границ
Правила целостности (например, «максимум 10 товаров в заказе») должны соблюдаться внутри агрегата.
- Ссылки только на корни других агрегатов
Агрегаты не должны хранить ссылки на внутренние объекты чужих агрегатов — только на их корни (через идентификаторы).
Пример:
Агрегат Заказ (корень) включает элементы заказа (OrderItem), адрес доставки и методы для добавления/удаления товаров. Внешние системы обращаются только к Order, а не к OrderItem напрямую.
Ошибки при работе с агрегатами:
- Слишком большие агрегаты
Если агрегат включает десятки сущностей, это усложняет транзакции и повышает риск конфликтов.
Решение: Дробите агрегаты, ориентируясь на бизнес-контекст.
- Нарушение инкапсуляции
Прямое изменение дочерних объектов в обход корня ломает целостность.
Решение: Скрывайте внутренние структуры (private поля, методы только для корня).
Как определить границы агрегата?
- Ищите транзакционные границы — что должно изменяться атомарно?
- Анализируйте бизнес-инварианты — какие правила связывают объекты?
- Избегайте «анаемичных» агрегатов — они должны содержать логику, а не быть просто набором данных.
———
Итого
Агрегаты в DDD — это не просто группировка классов, а способ защитить целостность домена и управлять сложностью.
Правильно выбранные границы агрегатов делают код:
- Более понятным,
- Устойчивым к ошибкам,
- Легким для масштабирования.
Главное правило:
Если сомневаетесь — делайте агрегаты меньше. Четкие границы спасут в долгосрочной перспективе!
P.s.
Полезная ссылка про агрегаты: https://dddinpython.hashnode.dev/mastering-aggregates-in-domain-driven-design
#DDD #Python #Aggregate #Entity #ValueObject #Программирование
В Domain-Driven Design (DDD) работа с сложными доменными моделями требует четкой структуры, чтобы избежать хаоса.
Агрегат (Aggregate) — один из ключевых паттернов DDD, который помогает управлять целостностью данных и упрощает взаимодействие с моделью.
Что такое Aggregate?
Агрегат — это кластер связанных объектов, рассматриваемых как единое целое.
Он объединяет:
1) Корень агрегата (Aggregate Root) — единственная точка входа для внешних взаимодействий.
2) Внутренние сущности (Entity) и value-объекты (ValueObject) — элементы, которые могут изменяться только через корень.
По своей сути агрегат - это тоже Entity.
Зачем нужны агрегаты?
- Консистентность данных
Агрегат гарантирует, что изменения внутри него соответствуют бизнес-правилам. Например, нельзя добавить OrderItem с отрицательной ценой — корень проверяет это.
- Границы транзакций
Изменения в рамках одного агрегата обычно выполняются в одной транзакции. Это упрощает управление конкурентным доступом.
- Сокрытие сложности
Внешние системы не знают о внутренней структуре агрегата — они работают только с корнем.
Ключевые принципы проектирования:
- Единый корень
Все внешние запросы идут через корень. Если нужно изменить дочерний объект — делайте это через методы корня.
- Инварианты внутри границ
Правила целостности (например, «максимум 10 товаров в заказе») должны соблюдаться внутри агрегата.
- Ссылки только на корни других агрегатов
Агрегаты не должны хранить ссылки на внутренние объекты чужих агрегатов — только на их корни (через идентификаторы).
Пример:
Агрегат Заказ (корень) включает элементы заказа (OrderItem), адрес доставки и методы для добавления/удаления товаров. Внешние системы обращаются только к Order, а не к OrderItem напрямую.
Ошибки при работе с агрегатами:
- Слишком большие агрегаты
Если агрегат включает десятки сущностей, это усложняет транзакции и повышает риск конфликтов.
Решение: Дробите агрегаты, ориентируясь на бизнес-контекст.
- Нарушение инкапсуляции
Прямое изменение дочерних объектов в обход корня ломает целостность.
Решение: Скрывайте внутренние структуры (private поля, методы только для корня).
Как определить границы агрегата?
- Ищите транзакционные границы — что должно изменяться атомарно?
- Анализируйте бизнес-инварианты — какие правила связывают объекты?
- Избегайте «анаемичных» агрегатов — они должны содержать логику, а не быть просто набором данных.
———
Итого
Агрегаты в DDD — это не просто группировка классов, а способ защитить целостность домена и управлять сложностью.
Правильно выбранные границы агрегатов делают код:
- Более понятным,
- Устойчивым к ошибкам,
- Легким для масштабирования.
Главное правило:
Если сомневаетесь — делайте агрегаты меньше. Четкие границы спасут в долгосрочной перспективе!
P.s.
Полезная ссылка про агрегаты: https://dddinpython.hashnode.dev/mastering-aggregates-in-domain-driven-design
#DDD #Python #Aggregate #Entity #ValueObject #Программирование
[Памятка: как делать правильные тесты]
Раньше я писал юнит-тесты как будто "на автомате" — просто покрывал методы, проверял, что возвращается нужное значение, и считал, что всё ок.
Но это не совсем правильно. Тестировать методы ради покрытия — выглядит логично, но часто оказывается поверхностным и ненадёжным.
Например, я тестирую имплементацию, а не поведение.
Но при рефакторинге метод может быть переписан — и старый тест либо ломается (хотя логика осталась верной), либо ничего не заметит, если баг спрятался глубже.
Невозможно быстро понять по тестам, что именно гарантирует система.
Тесты по методам не объясняют, что система должна делать — только что она делает сейчас.
А свойства — это живое описание инвариантов кода. И это крайне важно.
Правильный подход — тестировать свойства поведения фичи, которые должны сохраняться независимо от реализации.
Вот что я теперь делаю:
1) Останавливаюсь и думаю: что вообще значит "правильно работает"?
Не метод за методом, а вся фича в целом.
2) Формулирую свойства корректности:
Например, "если policy создана — она должна быть доступна", "удаление policy делает её недоступной", и т.д.
3) Для каждого свойства выбираю способ тестирования:
Если можно покрыть юнит-тестами — пишу их. Если нужно — делаю фаззинг или просто руками проверяю.
4) Пишу тесты, которые проверяют именно эти свойства, а не случайные детали реализации.
Что изменилось?
- Тесты стало проще поддерживать: они не ломаются из-за мелких рефакторингов.
- Я лучше понимаю, зачем вообще эта фича нужна, а не просто "чтобы метод не падал".
- Реже ловлю баги в проде — потому что я реально проверяю смысл, а не поведение "по случайке".
#unittests #python #testdesign #tests
Раньше я писал юнит-тесты как будто "на автомате" — просто покрывал методы, проверял, что возвращается нужное значение, и считал, что всё ок.
Но это не совсем правильно. Тестировать методы ради покрытия — выглядит логично, но часто оказывается поверхностным и ненадёжным.
Например, я тестирую имплементацию, а не поведение.
Но при рефакторинге метод может быть переписан — и старый тест либо ломается (хотя логика осталась верной), либо ничего не заметит, если баг спрятался глубже.
Невозможно быстро понять по тестам, что именно гарантирует система.
Тесты по методам не объясняют, что система должна делать — только что она делает сейчас.
А свойства — это живое описание инвариантов кода. И это крайне важно.
Правильный подход — тестировать свойства поведения фичи, которые должны сохраняться независимо от реализации.
Вот что я теперь делаю:
1) Останавливаюсь и думаю: что вообще значит "правильно работает"?
Не метод за методом, а вся фича в целом.
2) Формулирую свойства корректности:
Например, "если policy создана — она должна быть доступна", "удаление policy делает её недоступной", и т.д.
3) Для каждого свойства выбираю способ тестирования:
Если можно покрыть юнит-тестами — пишу их. Если нужно — делаю фаззинг или просто руками проверяю.
4) Пишу тесты, которые проверяют именно эти свойства, а не случайные детали реализации.
Что изменилось?
- Тесты стало проще поддерживать: они не ломаются из-за мелких рефакторингов.
- Я лучше понимаю, зачем вообще эта фича нужна, а не просто "чтобы метод не падал".
- Реже ловлю баги в проде — потому что я реально проверяю смысл, а не поведение "по случайке".
#unittests #python #testdesign #tests
[Репозитории в DDD: как правильно работать с данными в доменном слое]
Привет! Сегодня разберём ещё одну важную концепцию Domain-Driven Design — Репозитории (Repositories). Когда я только начал изучать DDD, репозитории казались мне просто "прослойкой над базой данных". Давайте разберёмся, зачем они нужны и как их правильно использовать!
Репозиторий (Repository) — это абстракция, которая:
✔️ Инкапсулирует доступ к данным (БД, API, файлы и т. д.).
✔️ Предоставляет "коллекцию" доменных объектов (как если бы они хранились в памяти).
✔️ Работает только с Aggregate Roots (не с отдельными Entity или Value Object!).
Пример из жизни:
Если
- Сохранять заказ (save).
- Загружать заказ по ID (find_by_id).
- Искать заказы по критериям (find_all_by_customer).
Но он НЕ даёт прямого доступа к
Ниже пример кода.
Почему репозитории — это важно?
1) Отделение домена от инфраструктуры
Домен не знает, как хранятся данные (БД, API, CSV и т. д.).
2) Гибкость
Можно легко поменять базу данных, не трогая доменную логику.
3) Соблюдение DDD-принципов
Работа только с агрегатами, а не с отдельными таблицами БД.
Где должны находиться репозитории в структуре проекта?
Репозитории относятся к инфраструктурному слою, но их интерфейсы (абстракции) определяются в доменном слое.
Ниже пример классической структуры проекта по слоям DDD.
```bash
src/
├── domain/
│ ├── models/
│ ├── repositories/
│ └── services/
│
├── infrastructure/
│ ├── db/
│ ├── repositories/
│ └── caching/
│
├── application/
│ └── services/
│
└── presentation/
```
Типичные ошибки
❌ Репозиторий для каждой Entity
Репозиторий должен быть только для Aggregate Root!
❌ Возвращение DTO вместо доменных объектов
Репозиторий должен возвращать
❌ Нарушение CQRS
Для запросов (Read) иногда лучше использовать отдельные запросы в cлое приложения, а не репозитории.
P.s.
Полезные ссылки по теме:
1) https://dddinpython.hashnode.dev/implementing-the-repository-pattern
2) https://dddinpython.hashnode.dev/implementing-the-repository-pattern-using-sqlalchemy
#DDD #Repository #DomainDrivenDesign #Программирование #Python
Привет! Сегодня разберём ещё одну важную концепцию Domain-Driven Design — Репозитории (Repositories). Когда я только начал изучать DDD, репозитории казались мне просто "прослойкой над базой данных". Давайте разберёмся, зачем они нужны и как их правильно использовать!
Репозиторий (Repository) — это абстракция, которая:
✔️ Инкапсулирует доступ к данным (БД, API, файлы и т. д.).
✔️ Предоставляет "коллекцию" доменных объектов (как если бы они хранились в памяти).
✔️ Работает только с Aggregate Roots (не с отдельными Entity или Value Object!).
Пример из жизни:
Если
Order
— это агрегат, то OrderRepository позволяет:- Сохранять заказ (save).
- Загружать заказ по ID (find_by_id).
- Искать заказы по критериям (find_all_by_customer).
Но он НЕ даёт прямого доступа к
OrderItem
— только через корень (Order
).Ниже пример кода.
from abc import ABC, abstractmethod
from uuid import UUID
from typing import List, Optional
from .order import Order # Агрегат
class OrderRepository(ABC):
"""Абстракция репозитория"""
@abstractmethod
def save(self, order: Order) -> None:
pass
@abstractmethod
def find_by_id(self, order_id: UUID) -> Optional[Order]:
pass
@abstractmethod
def find_all_by_customer(self, customer_id: UUID) -> List[Order]:
pass
from sqlalchemy.orm import Session
class SQLOrderRepository(OrderRepository):
"""SQL реализация (через SQLAlchemy)"""
def __init__(self, session: Session):
self.session = session
def save(self, order: Order) -> None:
self.session.add(order)
self.session.commit()
def find_by_id(self, order_id: UUID) -> Optional[Order]:
return self.session.query(Order).filter_by(order_id=order_id).first()
def find_all_by_customer(self, customer_id: UUID) -> List[Order]:
return self.session.query(Order).filter_by(customer_id=customer_id).all()
Почему репозитории — это важно?
1) Отделение домена от инфраструктуры
Домен не знает, как хранятся данные (БД, API, CSV и т. д.).
2) Гибкость
Можно легко поменять базу данных, не трогая доменную логику.
3) Соблюдение DDD-принципов
Работа только с агрегатами, а не с отдельными таблицами БД.
Где должны находиться репозитории в структуре проекта?
Репозитории относятся к инфраструктурному слою, но их интерфейсы (абстракции) определяются в доменном слое.
Ниже пример классической структуры проекта по слоям DDD.
```bash
src/
├── domain/
│ ├── models/
│ ├── repositories/
│ └── services/
│
├── infrastructure/
│ ├── db/
│ ├── repositories/
│ └── caching/
│
├── application/
│ └── services/
│
└── presentation/
```
Типичные ошибки
❌ Репозиторий для каждой Entity
Репозиторий должен быть только для Aggregate Root!
❌ Возвращение DTO вместо доменных объектов
Репозиторий должен возвращать
Order
, а не OrderData
.❌ Нарушение CQRS
Для запросов (Read) иногда лучше использовать отдельные запросы в cлое приложения, а не репозитории.
P.s.
Полезные ссылки по теме:
1) https://dddinpython.hashnode.dev/implementing-the-repository-pattern
2) https://dddinpython.hashnode.dev/implementing-the-repository-pattern-using-sqlalchemy
#DDD #Repository #DomainDrivenDesign #Программирование #Python
[Use Case в DDD: как проектировать сценарии взаимодействия с системой]
Привет! Сегодня поговорим про Use Case (Сценарии использования) — один из ключевых элементов DDD и чистой архитектуры.
1. Что такое Use Case?
Use Case (Сценарий использования) — это:
✔️ Описание конкретного бизнес-действия (например, "Оформление заказа", "Отмена бронирования").
✔️ Изолированная логика, которая координирует работу домена и инфраструктуры.
✔️ Мост между presentation (API/UI) и domain (бизнес-правила).
Примеры:
2. Где находятся Use Case в структуре проекта?
Они относятся к application-слою (слое приложения):
3. Пример use case "Создание заказа"
4. Почему Use Case — это важно?
- Чёткое разделение ответственности
Presentation-слой (API/CLI) не должен содержать бизнес-логику.
Domain-слой не должен знать о внешних сервисах (API, БД).
- Упрощение тестирования
Use Case можно тестировать изолированно, подменяя репозитории на заглушки.
- Гибкость
Один Use Case может быть вызван из разных мест: API, CLI, фоновой задачи.
- Документирование системы
Названия Use Cases (CreateOrder, CancelBooking) явно описывают, что делает система.
5. Типичные ошибки
❌ Use Case = "God Object"
Не нужно пихать всю логику в один Use Case. Разбивайте на мелкие сценарии.
❌ Логика в контроллерах
Код вида
#DDD #UseCase #CleanArchitecture #Python
Привет! Сегодня поговорим про Use Case (Сценарии использования) — один из ключевых элементов DDD и чистой архитектуры.
1. Что такое Use Case?
Use Case (Сценарий использования) — это:
✔️ Описание конкретного бизнес-действия (например, "Оформление заказа", "Отмена бронирования").
✔️ Изолированная логика, которая координирует работу домена и инфраструктуры.
✔️ Мост между presentation (API/UI) и domain (бизнес-правила).
Примеры:
CreateOrderUseCase
→ создание заказа.CancelBookingUseCase
→ отмена бронирования.ProcessPaymentUseCase
→ обработка платежа.2. Где находятся Use Case в структуре проекта?
Они относятся к application-слою (слое приложения):
src/
├── domain/
├── application/ # Use Cases + Application Services/CQS
│ ├── use_cases/
│ │ ├── commands/
│ │ └── queries/
│ └── services/
├── infrastructure/
└── api/
3. Пример use case "Создание заказа"
# /delivery/core/application/use_cases/commands/create_order/create_order_handler.py
from dataclasses import dataclass
from core.domain.model.order_aggregate.order import Order
from core.domain.model.shared_kernel.location import Location
from core.application.use_cases.commands.create_order.create_order_command import CreateOrderCommand
from infrastructure.adapters.postgres.unit_of_work import UnitOfWork
@dataclass
class CreateOrderCommandHandler:
unit_of_work: UnitOfWork
def handle(self, massege: CreateOrderCommand) -> None:
# Получаем геопозицию из Geo (пока ставим рандомное значение)
location = Location.create_random_location()
# Создаем заказ
order = Order(
order_id=massege.basket_id, # ID заказа совпадает с ID корзины
location=location
)
# Сохраняем
with self.unit_of_work as uow:
uow.orders.add(order)
4. Почему Use Case — это важно?
- Чёткое разделение ответственности
Presentation-слой (API/CLI) не должен содержать бизнес-логику.
Domain-слой не должен знать о внешних сервисах (API, БД).
- Упрощение тестирования
Use Case можно тестировать изолированно, подменяя репозитории на заглушки.
- Гибкость
Один Use Case может быть вызван из разных мест: API, CLI, фоновой задачи.
- Документирование системы
Названия Use Cases (CreateOrder, CancelBooking) явно описывают, что делает система.
5. Типичные ошибки
❌ Use Case = "God Object"
Не нужно пихать всю логику в один Use Case. Разбивайте на мелкие сценарии.
❌ Логика в контроллерах
Код вида
if user.is_admin: ...
должен быть в Use Case или домене, не в API.#DDD #UseCase #CleanArchitecture #Python
[Input Adapters в DDD: как внешний мир общается с приложением]
Привет! Сегодня про Input Adapters — элемент гексагональной архитектуры (и DDD), который отвечает за преобразование внешних запросов (HTTP, CLI, события) в команды приложения.
1. Что это такое?
Input Adapter — это не только про REST-эндпоинты, а это единая точка входа для всех внешних взаимодействий.
Он:
- Парсит входящие данные (JSON, protobuf, CLI-аргументы).
- Валидирует их (например, через Pydantic).
- Вызывает Use Case (Command или Query).
- Не содержит бизнес-логики (это задача домена).
Примеры Input Adapters:
- REST API (FastAPI, Django View).
- CLI-команды (Click, Typer).
- Обработчики событий (Kafka Consumer).
- WebSocket-хэндлеры.
Ниже пример REST API
2. Где находятся Input Adapters в структуре проекта?
Они относятся к presentation-слою, но могут выноситься в отдельный модуль для сложных сценариев:
3. Почему Input Adapters — это важно?
- Изоляция домена
Домен не знает, откуда пришёл запрос (HTTP, Kafka или CLI).
- Гибкость
Можно добавить новый адаптер (например, gRPC), не меняя Use Case.
- Тестируемость
Адаптеры легко тестировать в изоляции (например, мокая Use Case).
- Согласованность
Все входные данные проходят одинаковую валидацию перед передачей в Use Case.
4. Типичные ошибки
❌ Бизнес-логика в адаптерах
Код вида if user.role == "admin" должен быть в домене, а не в REST-хэндлере.
❌ Прямая работа с репозиториями
Адаптер вызывает только Use Case, не лезет в БД напрямую.
5. Вывод
Input Adapters — это "входные ворота" приложения, которые:
🔹 Преобразуют внешние запросы в команды/запросы.
🔹 Не содержат логики (только парсинг и валидацию).
🔹 Делают систему гибкой к изменениям протоколов.
#DDD #CleanArchitecture #InputAdapters #Python
Привет! Сегодня про Input Adapters — элемент гексагональной архитектуры (и DDD), который отвечает за преобразование внешних запросов (HTTP, CLI, события) в команды приложения.
1. Что это такое?
Input Adapter — это не только про REST-эндпоинты, а это единая точка входа для всех внешних взаимодействий.
Он:
- Парсит входящие данные (JSON, protobuf, CLI-аргументы).
- Валидирует их (например, через Pydantic).
- Вызывает Use Case (Command или Query).
- Не содержит бизнес-логики (это задача домена).
Примеры Input Adapters:
- REST API (FastAPI, Django View).
- CLI-команды (Click, Typer).
- Обработчики событий (Kafka Consumer).
- WebSocket-хэндлеры.
Ниже пример REST API
# presentation/api/rest/order_adapters.py
from fastapi import APIRouter, Depends
from application.commands import CreateOrderCommand
from presentation.schemas import OrderCreateRequest
router = APIRouter()
@router.post("/orders")
async def create_order(
request: OrderCreateRequest, # Pydantic-модель для валидации
command: CreateOrderCommand = Depends(), # Use Case
):
# Преобразуем запрос в DTO (без бизнес-логики!)
order_id = command.execute(
user_id=request.user_id,
items=[item.to_domain() for item in request.items]
)
return {"order_id": order_id}
2. Где находятся Input Adapters в структуре проекта?
Они относятся к presentation-слою, но могут выноситься в отдельный модуль для сложных сценариев:
src/
├── domain/
├── application/
├── infrastructure/
└── presentation/ # Input Adapters
├── api/ # HTTP-адаптеры
│ ├── rest/ # REST (FastAPI)
│ └── graphql/ # GraphQL
├── cli/ # CLI-адаптеры (Click)
└── events/ # Обработчики событий (Kafka, RabbitMQ)
3. Почему Input Adapters — это важно?
- Изоляция домена
Домен не знает, откуда пришёл запрос (HTTP, Kafka или CLI).
- Гибкость
Можно добавить новый адаптер (например, gRPC), не меняя Use Case.
- Тестируемость
Адаптеры легко тестировать в изоляции (например, мокая Use Case).
- Согласованность
Все входные данные проходят одинаковую валидацию перед передачей в Use Case.
4. Типичные ошибки
❌ Бизнес-логика в адаптерах
Код вида if user.role == "admin" должен быть в домене, а не в REST-хэндлере.
❌ Прямая работа с репозиториями
Адаптер вызывает только Use Case, не лезет в БД напрямую.
5. Вывод
Input Adapters — это "входные ворота" приложения, которые:
🔹 Преобразуют внешние запросы в команды/запросы.
🔹 Не содержат логики (только парсинг и валидацию).
🔹 Делают систему гибкой к изменениям протоколов.
#DDD #CleanArchitecture #InputAdapters #Python