Принцип инверсии зависимостей в проектировании, управляемом доменом
Мир быстро меняется. С этим изменением приходят новые бизнес-требования, с новыми требованиями приходят новые функции, а с новыми функциями приходит боль от попыток внести изменения в существующую кодовую базу, не нарушая ничего, что в настоящее время работает в производстве.
Все шаблоны проектирования программирования так или иначе пытаются сделать эти изменения как можно более плавными. Мы хотим, чтобы наш код был модульным, чтобы изменения были локализованы. Мы хотим избежать дублирования кода, чтобы изменения не нужно было вносить в несколько мест.
Мы не хотим, чтобы наша бизнес-логика зависела от внешней библиотеки, потому что, если нам нужно изменить указанную библиотеку, нам придется открывать модуль и всю его сложность. Например, предположим, что мы использовали SqlAlchemy для выполнения операций CRUD с таблицей PostgreSQL, содержащей информацию, относящуюся к заказам клиентов.
from sqlalchemy import Column, Integer, String, Date
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Order(Base):
__tablename__ = "orders"
id = Column(Integer, primary_key=True)
order_date = Column(Date)
bucket_type = Column(String)
status = Column(String)
Младшие программисты часто используют классы библиотек баз данных непосредственно в сервисах (т. е. в бизнес-логике). Хотя это может показаться не большой проблемой при использовании SQLAlchemy, поскольку он может взаимодействовать с несколькими базами данных, это может быть для определенных SDK, которые взаимодействуют с базами данных, такими как Trino, DynamoDB и т. д. Предположим, что мы решили отказаться от DynamoDB из-за бюджетных ограничений. , было бы сложно внести изменения, поскольку некоторые преобразования данных могли быть выполнены с использованием API SDK, и теперь их необходимо переписать с помощью vanilla Python или другой библиотеки.
Именно по этой причине мы пишем всю нашу основную бизнес-логику в модуле без каких-либо зависимостей, а затем используем принцип инверсии зависимостей для связи с уровнем данных/инфраструктуры.
Возвращаясь к нашему предыдущему примеру, вместо того, чтобы использовать объекты, производные от класса Base из библиотеки SQLAlchemy, мы бы определили объект домена, используя vanilla python.
# model.py
@dataclass(unsafe_hash=True)
class Order:
order_id: int
order_date: date
bucket_type: str
status: str
@classmethod
def from_dict(cls, d):
return cls(**d)
def as_dict(self):
return asdict(self)
def to_dict(self):
return json.dumps(asdict(self), default=str)
Затем в модулях более высокого уровня мы будем использовать объектно-реляционный преобразователь для сопоставления доменного объекта с конструкцией SQLAlchemy.
# orm.py
orders = Table(
"orders",
metadata,
Column("bucket_type", String(100), nullable=False),
Column("status", String(100), nullable=False),
Column("order_date", Date, nullable=False),
Column("order_id", Integer, nullable=False, primary_key=True)
)
def start_mappers():
mapper(Order, orders)
Основной модуль будет ссылаться на абстрактный репозиторий.
class AbstractOrdersRepository(abc.ABC):
@abc.abstractmethod
def find(self, **kwargs):
raise NotImplementedError
@abc.abstractmethod
def add(self, order: Order):
raise NotImplementedError
@abc.abstractmethod
def delete(self, order: Order):
raise NotImplementedError
В модуле более высокого уровня мы определяем конкретную реализацию абстрактного репозитория. Нижний модуль не знает об используемой нами базе данных. Он просто знает, как использовать метод добавления репозитория для сохранения данных. Таким образом, мы могли бы легко заменить репозиторий SQLAlchemy на что-то другое.
Для получения дополнительной информации ознакомьтесь с моим предыдущим постом о шаблоне проектирования репозитория.
Повышение уровня кодирования
Спасибо, что являетесь частью нашего сообщества! Перед тем, как ты уйдешь:
- 👏 Хлопайте за историю и подписывайтесь на автора 👉
- 📰 Смотрите больше контента в публикации Level Up Coding
- 💰 Бесплатный курс собеседования по программированию ⇒ Просмотреть курс
- 🔔 Подписывайтесь на нас: Twitter | ЛинкедИн | "Новостная рассылка"
🚀👉 Присоединяйтесь к коллективу талантов Level Up и найдите прекрасную работу