From a83b5ef2f879a17a5734432fb747d93d16a55cf2 Mon Sep 17 00:00:00 2001 From: Shahriyar Rzayev Date: Sun, 23 Oct 2022 12:38:17 +0400 Subject: [PATCH] feat: implement separate unit of work pattern --- src/jobboard/adapters/db/unit_of_work.py | 50 ------------------- src/jobboard/adapters/repositories/jobs.py | 6 +-- src/jobboard/adapters/repositories/users.py | 6 +-- src/jobboard/adapters/unit_of_works/jobs.py | 26 ++++++++++ src/jobboard/adapters/unit_of_works/users.py | 26 ++++++++++ src/jobboard/adapters/use_cases/jobs.py | 2 +- src/jobboard/adapters/use_cases/users.py | 2 +- src/jobboard/configurator/containers.py | 6 +-- .../domain/ports/unit_of_works/jobs.py | 32 ++++++++++++ .../users.py} | 31 +----------- src/jobboard/domain/ports/use_cases/jobs.py | 2 +- src/jobboard/domain/ports/use_cases/users.py | 2 +- tests/fake_container.py | 6 +-- 13 files changed, 97 insertions(+), 100 deletions(-) delete mode 100644 src/jobboard/adapters/db/unit_of_work.py create mode 100644 src/jobboard/adapters/unit_of_works/jobs.py create mode 100644 src/jobboard/adapters/unit_of_works/users.py create mode 100644 src/jobboard/domain/ports/unit_of_works/jobs.py rename src/jobboard/domain/ports/{unit_of_work.py => unit_of_works/users.py} (52%) diff --git a/src/jobboard/adapters/db/unit_of_work.py b/src/jobboard/adapters/db/unit_of_work.py deleted file mode 100644 index d9bebe6..0000000 --- a/src/jobboard/adapters/db/unit_of_work.py +++ /dev/null @@ -1,50 +0,0 @@ -from typing import Any, Callable - -from sqlalchemy.orm.session import Session - -from src.jobboard.adapters.repositories.users import UserSqlAlchemyRepository -from src.jobboard.adapters.repositories.jobs import JobSqlAlchemyRepository -from src.jobboard.domain.ports.unit_of_work import ( - JobUnitOfWorkInterface, - UserUnitOfWorkInterface, -) - - -class UserSqlAlchemyUnitOfWork(UserUnitOfWorkInterface): - def __init__(self, session_factory: Callable[[], Any]): - self.session_factory = session_factory() - - def __enter__(self): - self.session = self.session_factory() # type: Session - self.users = UserSqlAlchemyRepository(self.session) - return super().__enter__() - - def __exit__(self, *args): - super().__exit__(*args) - self.session.close() - - def _commit(self): - self.session.commit() - - def rollback(self): - self.session.rollback() - - -class JobSqlAlchemyUnitOfWork(JobUnitOfWorkInterface): - def __init__(self, session_factory: Callable[[], Any]): - self.session_factory = session_factory() - - def __enter__(self): - self.session = self.session_factory() # type: Session - self.jobs = JobSqlAlchemyRepository(self.session) - return super().__enter__() - - def __exit__(self, *args): - super().__exit__(*args) - self.session.close() - - def _commit(self): - self.session.commit() - - def rollback(self): - self.session.rollback() diff --git a/src/jobboard/adapters/repositories/jobs.py b/src/jobboard/adapters/repositories/jobs.py index 04e7fcb..7c84ea2 100644 --- a/src/jobboard/adapters/repositories/jobs.py +++ b/src/jobboard/adapters/repositories/jobs.py @@ -1,7 +1,5 @@ from src.jobboard.domain.model import model -from src.jobboard.domain.ports.repositories.jobs import ( - JobRepositoryInterface, -) +from src.jobboard.domain.ports.repositories.jobs import JobRepositoryInterface class JobSqlAlchemyRepository(JobRepositoryInterface): @@ -25,4 +23,4 @@ def _get_all(self) -> list[model.Job]: return self.session.query(model.Job).all() def _search(self, query: str) -> list[model.Job]: - return self.session.query(model.Job).filter(model.Job.title.contains(query)) \ No newline at end of file + return self.session.query(model.Job).filter(model.Job.title.contains(query)) diff --git a/src/jobboard/adapters/repositories/users.py b/src/jobboard/adapters/repositories/users.py index cb6ff0c..df71748 100644 --- a/src/jobboard/adapters/repositories/users.py +++ b/src/jobboard/adapters/repositories/users.py @@ -1,7 +1,5 @@ from src.jobboard.domain.model import model -from src.jobboard.domain.ports.repositories.users import ( - UserRepositoryInterface, -) +from src.jobboard.domain.ports.repositories.users import UserRepositoryInterface class UserSqlAlchemyRepository(UserRepositoryInterface): @@ -19,4 +17,4 @@ def _get_by_email(self, email: str) -> model.User: return self.session.query(model.User).filter_by(email=email).first() def _get_by_id(self, id: int) -> model.User: - return self.session.query(model.User).filter_by(id=id).first() \ No newline at end of file + return self.session.query(model.User).filter_by(id=id).first() diff --git a/src/jobboard/adapters/unit_of_works/jobs.py b/src/jobboard/adapters/unit_of_works/jobs.py new file mode 100644 index 0000000..f95ebe2 --- /dev/null +++ b/src/jobboard/adapters/unit_of_works/jobs.py @@ -0,0 +1,26 @@ +from typing import Any, Callable + +from sqlalchemy.orm.session import Session + +from src.jobboard.adapters.repositories.jobs import JobSqlAlchemyRepository +from src.jobboard.domain.ports.unit_of_works.jobs import JobUnitOfWorkInterface + + +class JobSqlAlchemyUnitOfWork(JobUnitOfWorkInterface): + def __init__(self, session_factory: Callable[[], Any]): + self.session_factory = session_factory() + + def __enter__(self): + self.session = self.session_factory() # type: Session + self.jobs = JobSqlAlchemyRepository(self.session) + return super().__enter__() + + def __exit__(self, *args): + super().__exit__(*args) + self.session.close() + + def _commit(self): + self.session.commit() + + def rollback(self): + self.session.rollback() diff --git a/src/jobboard/adapters/unit_of_works/users.py b/src/jobboard/adapters/unit_of_works/users.py new file mode 100644 index 0000000..f4f293f --- /dev/null +++ b/src/jobboard/adapters/unit_of_works/users.py @@ -0,0 +1,26 @@ +from typing import Any, Callable + +from sqlalchemy.orm.session import Session + +from src.jobboard.adapters.repositories.users import UserSqlAlchemyRepository +from src.jobboard.domain.ports.unit_of_works.users import UserUnitOfWorkInterface + + +class UserSqlAlchemyUnitOfWork(UserUnitOfWorkInterface): + def __init__(self, session_factory: Callable[[], Any]): + self.session_factory = session_factory() + + def __enter__(self): + self.session = self.session_factory() # type: Session + self.users = UserSqlAlchemyRepository(self.session) + return super().__enter__() + + def __exit__(self, *args): + super().__exit__(*args) + self.session.close() + + def _commit(self): + self.session.commit() + + def rollback(self): + self.session.rollback() diff --git a/src/jobboard/adapters/use_cases/jobs.py b/src/jobboard/adapters/use_cases/jobs.py index 1929e06..e23f86b 100644 --- a/src/jobboard/adapters/use_cases/jobs.py +++ b/src/jobboard/adapters/use_cases/jobs.py @@ -6,7 +6,7 @@ ResponseSuccess, ResponseTypes, ) -from src.jobboard.domain.ports.unit_of_work import JobUnitOfWorkInterface +from src.jobboard.domain.ports.unit_of_works.jobs import JobUnitOfWorkInterface from src.jobboard.domain.ports.use_cases.jobs import JobServiceInterface from src.jobboard.domain.schemas.jobs import JobCreateInputDto, JobOutputDto diff --git a/src/jobboard/adapters/use_cases/users.py b/src/jobboard/adapters/use_cases/users.py index 1b2a141..e10d11f 100644 --- a/src/jobboard/adapters/use_cases/users.py +++ b/src/jobboard/adapters/use_cases/users.py @@ -7,7 +7,7 @@ ResponseSuccess, ResponseTypes, ) -from src.jobboard.domain.ports.unit_of_work import UserUnitOfWorkInterface +from src.jobboard.domain.ports.unit_of_works.users import UserUnitOfWorkInterface from src.jobboard.domain.ports.use_cases.users import UserServiceInterface from src.jobboard.domain.schemas.users import ( UserCreateInputDto, diff --git a/src/jobboard/configurator/containers.py b/src/jobboard/configurator/containers.py index 4450810..13dce22 100644 --- a/src/jobboard/configurator/containers.py +++ b/src/jobboard/configurator/containers.py @@ -2,10 +2,8 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from src.jobboard.adapters.db.unit_of_work import ( - JobSqlAlchemyUnitOfWork, - UserSqlAlchemyUnitOfWork, -) +from src.jobboard.adapters.unit_of_works.jobs import JobSqlAlchemyUnitOfWork +from src.jobboard.adapters.unit_of_works.users import UserSqlAlchemyUnitOfWork from src.jobboard.adapters.use_cases.jobs import JobService from src.jobboard.adapters.use_cases.users import UserService from src.jobboard.configurator import config diff --git a/src/jobboard/domain/ports/unit_of_works/jobs.py b/src/jobboard/domain/ports/unit_of_works/jobs.py new file mode 100644 index 0000000..af7739a --- /dev/null +++ b/src/jobboard/domain/ports/unit_of_works/jobs.py @@ -0,0 +1,32 @@ +import abc + +from src.jobboard.domain.ports.common import messagebus +from src.jobboard.domain.ports.repositories.jobs import JobRepositoryInterface + + +class JobUnitOfWorkInterface(abc.ABC): + jobs: JobRepositoryInterface + + def __enter__(self) -> "JobUnitOfWorkInterface": + return self + + def __exit__(self, *args): + self.rollback() + + def commit(self): + self._commit() + self.publish_events() + + def publish_events(self): + for job in self.jobs.seen: + while job.events: + event = job.events.pop(0) + messagebus.handle(event) + + @abc.abstractmethod + def _commit(self): + raise NotImplementedError + + @abc.abstractmethod + def rollback(self): + raise NotImplementedError diff --git a/src/jobboard/domain/ports/unit_of_work.py b/src/jobboard/domain/ports/unit_of_works/users.py similarity index 52% rename from src/jobboard/domain/ports/unit_of_work.py rename to src/jobboard/domain/ports/unit_of_works/users.py index e91f920..cd54e84 100644 --- a/src/jobboard/domain/ports/unit_of_work.py +++ b/src/jobboard/domain/ports/unit_of_works/users.py @@ -1,8 +1,7 @@ import abc -from src.jobboard.domain.ports.repositories.users import UserRepositoryInterface -from src.jobboard.domain.ports.repositories.jobs import JobRepositoryInterface from src.jobboard.domain.ports.common import messagebus +from src.jobboard.domain.ports.repositories.users import UserRepositoryInterface class UserUnitOfWorkInterface(abc.ABC): @@ -31,31 +30,3 @@ def _commit(self): @abc.abstractmethod def rollback(self): raise NotImplementedError - - -class JobUnitOfWorkInterface(abc.ABC): - jobs: JobRepositoryInterface - - def __enter__(self) -> "JobUnitOfWorkInterface": - return self - - def __exit__(self, *args): - self.rollback() - - def commit(self): - self._commit() - self.publish_events() - - def publish_events(self): - for job in self.jobs.seen: - while job.events: - event = job.events.pop(0) - messagebus.handle(event) - - @abc.abstractmethod - def _commit(self): - raise NotImplementedError - - @abc.abstractmethod - def rollback(self): - raise NotImplementedError diff --git a/src/jobboard/domain/ports/use_cases/jobs.py b/src/jobboard/domain/ports/use_cases/jobs.py index ce03b91..3a1047f 100644 --- a/src/jobboard/domain/ports/use_cases/jobs.py +++ b/src/jobboard/domain/ports/use_cases/jobs.py @@ -2,7 +2,7 @@ from typing import Union from src.jobboard.domain.ports.common.responses import ResponseFailure, ResponseSuccess -from src.jobboard.domain.ports.unit_of_work import JobUnitOfWorkInterface +from src.jobboard.domain.ports.unit_of_works.jobs import JobUnitOfWorkInterface from src.jobboard.domain.schemas.jobs import JobCreateInputDto diff --git a/src/jobboard/domain/ports/use_cases/users.py b/src/jobboard/domain/ports/use_cases/users.py index 5175acf..bf69d2f 100644 --- a/src/jobboard/domain/ports/use_cases/users.py +++ b/src/jobboard/domain/ports/use_cases/users.py @@ -2,7 +2,7 @@ from typing import Union from src.jobboard.domain.ports.common.responses import ResponseFailure, ResponseSuccess -from src.jobboard.domain.ports.unit_of_work import UserUnitOfWorkInterface +from src.jobboard.domain.ports.unit_of_works.users import UserUnitOfWorkInterface from src.jobboard.domain.schemas.users import ( UserCreateInputDto, UserLoginInputDto, diff --git a/tests/fake_container.py b/tests/fake_container.py index 073cd7a..bd3e370 100644 --- a/tests/fake_container.py +++ b/tests/fake_container.py @@ -2,10 +2,8 @@ from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -from src.jobboard.adapters.db.unit_of_work import ( - JobSqlAlchemyUnitOfWork, - UserSqlAlchemyUnitOfWork, -) +from src.jobboard.adapters.unit_of_works.jobs import JobSqlAlchemyUnitOfWork +from src.jobboard.adapters.unit_of_works.users import UserSqlAlchemyUnitOfWork from src.jobboard.adapters.use_cases.jobs import JobService from src.jobboard.adapters.use_cases.users import UserService