Skip to content

Commit 708cb81

Browse files
DDD refactor: domain-based structure + dependency injection
- Restructured to DDD domains (user/, auth/, admin/, core/) - Renamed src/ → app/ to match FastAPI conventions - Removed all src.* import prefixes - Converted services to DI pattern (removed @staticmethod) - Created dependencies.py in each domain for service injection - Updated all routes to inject services via Depends() - Kept capital letter model naming (User.py, RefreshToken.py) - Updated Docker, alembic, and test files - Set PYTHONPATH=/app/app for clean imports Import style: - Relative within domain: from .User import User - No prefix for core: from core.database import ... - Domain name for cross-domain: from user.repository import ...
1 parent 0ddc168 commit 708cb81

File tree

14 files changed

+149
-125
lines changed

14 files changed

+149
-125
lines changed

backend/alembic/env.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
from sqlalchemy.ext.asyncio import async_engine_from_config
1212

1313
from config import settings
14-
from models import Base
14+
from core.Base import Base
1515
from core.enums import SafeEnum
16+
from user.User import User
17+
from auth.RefreshToken import RefreshToken
1618

1719

1820
config = context.config

backend/app/admin/routes.py

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@
1717
settings,
1818
UserRole,
1919
)
20-
from core.dependencies import (
21-
DBSession,
22-
RequireRole,
23-
)
20+
from core.dependencies import RequireRole
2421
from core.responses import (
2522
AUTH_401,
2623
CONFLICT_409,
@@ -34,7 +31,7 @@
3431
UserUpdateAdmin,
3532
)
3633
from user.User import User
37-
from user.service import UserService
34+
from user.dependencies import UserServiceDep
3835

3936

4037
router = APIRouter(prefix = "/admin", tags = ["admin"])
@@ -51,7 +48,7 @@
5148
},
5249
)
5350
async def list_users(
54-
db: DBSession,
51+
user_service: UserServiceDep,
5552
_: AdminOnly,
5653
page: int = Query(default = 1,
5754
ge = 1),
@@ -64,7 +61,7 @@ async def list_users(
6461
"""
6562
List all users (admin only)
6663
"""
67-
return await UserService.list_users(db, page, size)
64+
return await user_service.list_users(page, size)
6865

6966

7067
@router.post(
@@ -78,14 +75,14 @@ async def list_users(
7875
},
7976
)
8077
async def create_user(
81-
db: DBSession,
78+
user_service: UserServiceDep,
8279
_: AdminOnly,
8380
user_data: AdminUserCreate,
8481
) -> UserResponse:
8582
"""
8683
Create a new user (admin only, bypasses registration)
8784
"""
88-
return await UserService.admin_create_user(db, user_data)
85+
return await user_service.admin_create_user(user_data)
8986

9087

9188
@router.get(
@@ -98,14 +95,14 @@ async def create_user(
9895
},
9996
)
10097
async def get_user(
101-
db: DBSession,
98+
user_service: UserServiceDep,
10299
_: AdminOnly,
103100
user_id: UUID,
104101
) -> UserResponse:
105102
"""
106103
Get user by ID (admin only)
107104
"""
108-
return await UserService.get_user_by_id(db, user_id)
105+
return await user_service.get_user_by_id(user_id)
109106

110107

111108
@router.patch(
@@ -119,15 +116,15 @@ async def get_user(
119116
},
120117
)
121118
async def update_user(
122-
db: DBSession,
119+
user_service: UserServiceDep,
123120
_: AdminOnly,
124121
user_id: UUID,
125122
user_data: UserUpdateAdmin,
126123
) -> UserResponse:
127124
"""
128125
Update user (admin only)
129126
"""
130-
return await UserService.admin_update_user(db, user_id, user_data)
127+
return await user_service.admin_update_user(user_id, user_data)
131128

132129

133130
@router.delete(
@@ -140,11 +137,11 @@ async def update_user(
140137
},
141138
)
142139
async def delete_user(
143-
db: DBSession,
140+
user_service: UserServiceDep,
144141
_: AdminOnly,
145142
user_id: UUID,
146143
) -> None:
147144
"""
148145
Delete user (admin only, hard delete)
149146
"""
150-
await UserService.admin_delete_user(db, user_id)
147+
await user_service.admin_delete_user(user_id)

backend/app/auth/dependencies.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
"""
2+
ⒸAngelaMos | 2025
3+
dependencies.py
4+
"""
5+
6+
from typing import Annotated
7+
8+
from fastapi import Depends
9+
10+
from core.dependencies import DBSession
11+
from .service import AuthService
12+
13+
14+
def get_auth_service(db: DBSession) -> AuthService:
15+
"""
16+
Dependency to inject AuthService instance
17+
"""
18+
return AuthService(db)
19+
20+
21+
AuthServiceDep = Annotated[AuthService, Depends(get_auth_service)]

backend/app/auth/routes.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from core.dependencies import (
2222
ClientIP,
2323
CurrentUser,
24-
DBSession,
2524
)
2625
from core.security import (
2726
clear_refresh_cookie,
@@ -35,8 +34,8 @@
3534
TokenWithUserResponse,
3635
)
3736
from user.schemas import UserResponse
38-
from .service import AuthService
39-
from user.service import UserService
37+
from .dependencies import AuthServiceDep
38+
from user.dependencies import UserServiceDep
4039
from core.responses import AUTH_401
4140

4241

@@ -52,16 +51,15 @@
5251
async def login(
5352
request: Request,
5453
response: Response,
55-
db: DBSession,
54+
auth_service: AuthServiceDep,
5655
ip: ClientIP,
5756
form_data: Annotated[OAuth2PasswordRequestForm,
5857
Depends()],
5958
) -> TokenWithUserResponse:
6059
"""
6160
Login with email and password
6261
"""
63-
result, refresh_token = await AuthService.login(
64-
db,
62+
result, refresh_token = await auth_service.login(
6563
email=form_data.username,
6664
password=form_data.password,
6765
ip_address=ip,
@@ -76,7 +74,7 @@ async def login(
7674
responses = {**AUTH_401}
7775
)
7876
async def refresh_token(
79-
db: DBSession,
77+
auth_service: AuthServiceDep,
8078
ip: ClientIP,
8179
refresh_token: str | None = Cookie(None),
8280
) -> TokenResponse:
@@ -85,8 +83,7 @@ async def refresh_token(
8583
"""
8684
if not refresh_token:
8785
raise TokenError("Refresh token required")
88-
return await AuthService.refresh_tokens(
89-
db,
86+
return await auth_service.refresh_tokens(
9087
refresh_token,
9188
ip_address = ip
9289
)
@@ -99,29 +96,29 @@ async def refresh_token(
9996
)
10097
async def logout(
10198
response: Response,
102-
db: DBSession,
99+
auth_service: AuthServiceDep,
103100
refresh_token: str | None = Cookie(None),
104101
) -> None:
105102
"""
106103
Logout current session
107104
"""
108105
if not refresh_token:
109106
raise TokenError("Refresh token required")
110-
await AuthService.logout(db, refresh_token)
107+
await auth_service.logout(refresh_token)
111108
clear_refresh_cookie(response)
112109

113110

114111
@router.post("/logout-all", responses = {**AUTH_401})
115112
async def logout_all(
116113
response: Response,
117-
db: DBSession,
114+
auth_service: AuthServiceDep,
118115
current_user: CurrentUser,
119116
) -> dict[str,
120117
int]:
121118
"""
122119
Logout from all devices
123120
"""
124-
count = await AuthService.logout_all(db, current_user)
121+
count = await auth_service.logout_all(current_user)
125122
clear_refresh_cookie(response)
126123
return {"revoked_sessions": count}
127124

@@ -140,15 +137,14 @@ async def get_current_user(current_user: CurrentUser) -> UserResponse:
140137
responses = {**AUTH_401}
141138
)
142139
async def change_password(
143-
db: DBSession,
140+
user_service: UserServiceDep,
144141
current_user: CurrentUser,
145142
data: PasswordChange,
146143
) -> None:
147144
"""
148145
Change current user password
149146
"""
150-
await UserService.change_password(
151-
db,
147+
await user_service.change_password(
152148
current_user,
153149
data.current_password,
154150
data.new_password,

0 commit comments

Comments
 (0)