Repository: NEONKID/python-mf-data (language: python)

Issue:
BaseUseCase 생성을 통한 Transactional 데코레이터 보완 작업
```transactional``` 데코레이터가 특정 멤버 변수에 의존하여 UseCase 클래스를 만드는 데 있어 필수 항목으로 지정하여야 할 듯하다.
이에 따른 BaseUseCase 클래스를 만들어 사용할 수 있도록 확장성을 높여보자.


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
index 709fb0d..0992c37 100644
--- a/.github/workflows/pr.yml
+++ b/.github/workflows/pr.yml
@@ -4,6 +4,7 @@ on:
   pull_request:
     branches:
       - 'main'
+      - 'develop'
 
 jobs:
   mf_data_test:
diff --git a/README.ko.md b/README.ko.md
index ed9ce93..43ef2e3 100644
--- a/README.ko.md
+++ b/README.ko.md
@@ -202,7 +202,7 @@ SQLAlchemy에서 기본적으로 제공하는 작업 단위 패턴 외에 직접
 from sqlalchemy.engine import Engine
 from sqlalchemy.ext.asyncio import AsyncEngine
 
-from pymfdata.rdb.command import AsyncSQLAlchemyUnitOfWork, SyncSQLAlchemyUnitOfWork
+from pymfdata.rdb.usecase import AsyncSQLAlchemyUnitOfWork, SyncSQLAlchemyUnitOfWork
 from pymfdata.rdb.transaction import async_transactional
 
 
@@ -214,7 +214,7 @@ class AsyncMemoUseCaseUnitOfWork(AsyncSQLAlchemyUnitOfWork):
         await super().__aenter__()
 
         self.memo_repository: MemoRepository = MemoRepository(self.session)
-            
+
 
 class MemoUseCase:
     def __init__(self, uow: AsyncMemoUseCaseUnitOfWork) -> None:
diff --git a/README.md b/README.md
index 350a6c0..8428037 100644
--- a/README.md
+++ b/README.md
@@ -203,7 +203,7 @@ SQLAlchemy uses the unit of work pattern by default, but if you want to define y
 from sqlalchemy.engine import Engine
 from sqlalchemy.ext.asyncio import AsyncEngine
 
-from pymfdata.rdb.command import AsyncSQLAlchemyUnitOfWork, SyncSQLAlchemyUnitOfWork
+from pymfdata.rdb.usecase import AsyncSQLAlchemyUnitOfWork, SyncSQLAlchemyUnitOfWork
 from pymfdata.rdb.transaction import async_transactional
 
 
@@ -215,7 +215,7 @@ class AsyncMemoUseCaseUnitOfWork(AsyncSQLAlchemyUnitOfWork):
         await super().__aenter__()
 
         self.memo_repository: MemoRepository = MemoRepository(self.session)
-            
+
 
 class MemoUseCase:
     def __init__(self, uow: AsyncMemoUseCaseUnitOfWork) -> None:
diff --git a/pymfdata/common/command.py b/pymfdata/common/usecase.py
similarity index 82%
rename from pymfdata/common/command.py
rename to pymfdata/common/usecase.py
index c28afba..16c60a0 100644
--- a/pymfdata/common/command.py
+++ b/pymfdata/common/usecase.py
@@ -1,5 +1,5 @@
 from abc import ABC, abstractmethod
-from typing import Optional, Type
+from typing import final, Optional, Protocol, Type, Union
 
 
 class AsyncBaseUnitOfWork(ABC):
@@ -36,3 +36,12 @@ def commit(self):
     @abstractmethod
     def rollback(self):
         raise NotImplementedError("Choice ORM rollback func")
+
+
+class BaseUseCase(Protocol):
+    _uow: Union[AsyncBaseUnitOfWork, SyncBaseUnitOfWork]
+
+    @property
+    def uow(self):
+        assert self._uow is not None
+        return self._uow
diff --git a/pymfdata/rdb/transaction.py b/pymfdata/rdb/transaction.py
index 5e7e1c7..3eeb4b3 100644
--- a/pymfdata/rdb/transaction.py
+++ b/pymfdata/rdb/transaction.py
@@ -1,6 +1,6 @@
 import enum
 
-from pymfdata.rdb.command import AsyncSession, Session
+from pymfdata.rdb.usecase import AsyncSession, Session
 
 
 class Propagation(enum.Enum):
diff --git a/pymfdata/rdb/command.py b/pymfdata/rdb/usecase.py
similarity index 95%
rename from pymfdata/rdb/command.py
rename to pymfdata/rdb/usecase.py
index 0d2fdd4..9ec45d2 100644
--- a/pymfdata/rdb/command.py
+++ b/pymfdata/rdb/usecase.py
@@ -3,7 +3,7 @@
 from sqlalchemy.orm import Session
 from typing import Optional, Type
 
-from pymfdata.common.command import AsyncBaseUnitOfWork, SyncBaseUnitOfWork
+from pymfdata.common.usecase import AsyncBaseUnitOfWork, SyncBaseUnitOfWork
 
 
 class AsyncSQLAlchemyUnitOfWork(AsyncBaseUnitOfWork):


Test patch:
diff --git a/tests/rdb/domain/usecase.py b/tests/rdb/domain/usecase.py
index 683a82c..7ce3640 100644
--- a/tests/rdb/domain/usecase.py
+++ b/tests/rdb/domain/usecase.py
@@ -1,7 +1,8 @@
 from sqlalchemy.engine import Engine
 from sqlalchemy.ext.asyncio import AsyncEngine
 
-from pymfdata.rdb.command import AsyncSQLAlchemyUnitOfWork, SyncSQLAlchemyUnitOfWork
+from pymfdata.common.usecase import BaseUseCase
+from pymfdata.rdb.usecase import AsyncSQLAlchemyUnitOfWork, SyncSQLAlchemyUnitOfWork
 from pymfdata.rdb.transaction import async_transactional
 
 from tests.rdb.domain.dto import MemoRequest
@@ -28,9 +29,9 @@ def __enter__(self):
         self.memo_repository: SyncMemoRepository = SyncMemoRepository(self.session)
 
 
-class MemoUseCase:
+class MemoUseCase(BaseUseCase):
     def __init__(self, uow: AsyncMemoUseCaseUnitOfWork) -> None:
-        self.uow = uow
+        self._uow = uow
 
     @async_transactional(read_only=True)
     async def find_by_id(self, item_id: int):