backend-systems 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. backend_systems-1.0.0.dist-info/METADATA +186 -0
  2. backend_systems-1.0.0.dist-info/RECORD +38 -0
  3. backend_systems-1.0.0.dist-info/WHEEL +5 -0
  4. backend_systems-1.0.0.dist-info/top_level.txt +1 -0
  5. src/__init__.py +10 -0
  6. src/application/__init__.py +1 -0
  7. src/application/services/__init__.py +1 -0
  8. src/application/services/auth_service.py +216 -0
  9. src/application/services/order_service.py +272 -0
  10. src/config.py +92 -0
  11. src/domain/__init__.py +1 -0
  12. src/domain/entities/__init__.py +17 -0
  13. src/domain/entities/inventory.py +246 -0
  14. src/domain/entities/order.py +290 -0
  15. src/domain/entities/organization.py +136 -0
  16. src/domain/entities/user.py +143 -0
  17. src/domain/value_objects/__init__.py +7 -0
  18. src/domain/value_objects/address.py +96 -0
  19. src/domain/value_objects/email.py +90 -0
  20. src/domain/value_objects/money.py +215 -0
  21. src/infrastructure/__init__.py +1 -0
  22. src/infrastructure/database/__init__.py +1 -0
  23. src/infrastructure/database/models.py +281 -0
  24. src/infrastructure/database/session.py +96 -0
  25. src/infrastructure/repositories/__init__.py +1 -0
  26. src/infrastructure/repositories/base.py +158 -0
  27. src/infrastructure/repositories/inventory_repository.py +228 -0
  28. src/infrastructure/repositories/order_repository.py +211 -0
  29. src/infrastructure/repositories/user_repository.py +120 -0
  30. src/main.py +120 -0
  31. src/presentation/__init__.py +1 -0
  32. src/presentation/routes/__init__.py +1 -0
  33. src/presentation/routes/auth.py +217 -0
  34. src/presentation/routes/health.py +103 -0
  35. src/presentation/routes/inventory.py +346 -0
  36. src/presentation/routes/orders.py +340 -0
  37. src/presentation/routes/users.py +286 -0
  38. src/presentation/schemas/__init__.py +1 -0
@@ -0,0 +1,186 @@
1
+ Metadata-Version: 2.4
2
+ Name: backend-systems
3
+ Version: 1.0.0
4
+ Summary: ERP-style backend with DDD and Clean Architecture
5
+ Author-email: Engineering Team <team@example.com>
6
+ License: MIT
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Intended Audience :: Developers
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Requires-Python: >=3.11
13
+ Description-Content-Type: text/markdown
14
+ Requires-Dist: fastapi>=0.109.0
15
+ Requires-Dist: uvicorn[standard]>=0.27.0
16
+ Requires-Dist: sqlalchemy>=2.0.25
17
+ Requires-Dist: alembic>=1.13.1
18
+ Requires-Dist: psycopg2-binary>=2.9.9
19
+ Requires-Dist: pydantic>=2.5.3
20
+ Requires-Dist: pydantic-settings>=2.1.0
21
+ Requires-Dist: python-jose[cryptography]>=3.3.0
22
+ Requires-Dist: passlib[bcrypt]>=1.7.4
23
+ Requires-Dist: python-multipart>=0.0.6
24
+ Requires-Dist: httpx>=0.26.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=7.4.4; extra == "dev"
27
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
28
+ Requires-Dist: pytest-asyncio>=0.23.3; extra == "dev"
29
+ Requires-Dist: httpx>=0.26.0; extra == "dev"
30
+ Requires-Dist: black>=24.1.0; extra == "dev"
31
+ Requires-Dist: ruff>=0.1.14; extra == "dev"
32
+ Requires-Dist: mypy>=1.8.0; extra == "dev"
33
+
34
+ # 01_backend_systems - ERP-Style Backend
35
+
36
+ > Production-grade ERP backend demonstrating Domain-Driven Design, Clean Architecture, and enterprise patterns.
37
+
38
+ ## 🎯 Overview
39
+
40
+ This module implements a comprehensive ERP-style backend with:
41
+
42
+ - **Domain-Driven Design (DDD)** - Entities, Value Objects, Aggregates
43
+ - **Clean Architecture** - Layered separation of concerns
44
+ - **RBAC** - Role-based access control
45
+ - **RESTful APIs** - OpenAPI 3.0 documented
46
+ - **PostgreSQL** - With Alembic migrations
47
+
48
+ ## 📁 Structure
49
+
50
+ ```
51
+ 01_backend_systems/
52
+ ├── src/
53
+ │ ├── domain/ # Business logic & entities
54
+ │ │ ├── entities/ # Core domain models
55
+ │ │ └── value_objects/# Immutable value types
56
+ │ ├── application/ # Use cases & services
57
+ │ │ └── services/ # Application services
58
+ │ ├── infrastructure/ # External concerns
59
+ │ │ ├── database/ # DB connection & session
60
+ │ │ └── repositories/ # Data access layer
61
+ │ └── presentation/ # API layer
62
+ │ ├── routes/ # API endpoints
63
+ │ └── schemas/ # Pydantic models
64
+ ├── alembic/ # Database migrations
65
+ ├── tests/ # Unit & integration tests
66
+ └── example_data/ # Sample data for testing
67
+ ```
68
+
69
+ ## 🚀 Quick Start
70
+
71
+ ### Prerequisites
72
+
73
+ - Python 3.11+
74
+ - PostgreSQL 14+ (or use SQLite for development)
75
+
76
+ ### Installation
77
+
78
+ ```bash
79
+ # Create virtual environment
80
+ python -m venv venv
81
+ source venv/bin/activate # or `venv\Scripts\activate` on Windows
82
+
83
+ # Install dependencies
84
+ pip install -e .
85
+
86
+ # Set environment variables
87
+ cp .env.example .env
88
+ # Edit .env with your database credentials
89
+
90
+ # Run migrations
91
+ alembic upgrade head
92
+
93
+ # Start the server
94
+ uvicorn src.main:app --reload --port 8000
95
+ ```
96
+
97
+ ### API Documentation
98
+
99
+ Once running, visit:
100
+
101
+ - Swagger UI: <http://localhost:8000/docs>
102
+ - ReDoc: <http://localhost:8000/redoc>
103
+
104
+ ## 🏗️ Architecture
105
+
106
+ ```
107
+ ┌─────────────────────────────────────────────────────────────┐
108
+ │ PRESENTATION LAYER │
109
+ │ (FastAPI Routes, Pydantic Schemas, Request/Response DTOs) │
110
+ └─────────────────────────────────────────────────────────────┘
111
+ │
112
+ ▼
113
+ ┌─────────────────────────────────────────────────────────────┐
114
+ │ APPLICATION LAYER │
115
+ │ (Use Cases, Application Services, Business Logic) │
116
+ └─────────────────────────────────────────────────────────────┘
117
+ │
118
+ ▼
119
+ ┌─────────────────────────────────────────────────────────────┐
120
+ │ DOMAIN LAYER │
121
+ │ (Entities, Value Objects, Domain Services, Aggregates) │
122
+ └─────────────────────────────────────────────────────────────┘
123
+ │
124
+ ▼
125
+ ┌─────────────────────────────────────────────────────────────┐
126
+ │ INFRASTRUCTURE LAYER │
127
+ │ (Repositories, Database, External APIs, Messaging) │
128
+ └─────────────────────────────────────────────────────────────┘
129
+ ```
130
+
131
+ ## 📋 Domain Model
132
+
133
+ The ERP system manages:
134
+
135
+ - **Organizations** - Multi-tenant company management
136
+ - **Users** - Authentication and authorization
137
+ - **Inventory** - Product and stock management
138
+ - **Orders** - Sales order processing
139
+ - **Invoices** - Billing and payment tracking
140
+
141
+ ## 🔒 RBAC Roles
142
+
143
+ | Role | Permissions |
144
+ |------|-------------|
145
+ | `admin` | Full system access |
146
+ | `manager` | Read/write on assigned resources |
147
+ | `operator` | Limited write access |
148
+ | `viewer` | Read-only access |
149
+
150
+ ## 🧪 Testing
151
+
152
+ ```bash
153
+ # Run all tests
154
+ pytest tests/
155
+
156
+ # Run with coverage
157
+ pytest tests/ --cov=src --cov-report=html
158
+
159
+ # Run specific test file
160
+ pytest tests/unit/test_entities.py -v
161
+ ```
162
+
163
+ ## 📊 API Endpoints
164
+
165
+ | Method | Endpoint | Description |
166
+ |--------|----------|-------------|
167
+ | POST | `/api/v1/auth/login` | Authenticate user |
168
+ | GET | `/api/v1/users` | List users |
169
+ | POST | `/api/v1/orders` | Create order |
170
+ | GET | `/api/v1/inventory` | List inventory |
171
+ | GET | `/health` | Health check |
172
+
173
+ ## 🔧 Configuration
174
+
175
+ Environment variables (`.env`):
176
+
177
+ ```env
178
+ DATABASE_URL=postgresql://user:pass@localhost:5432/erp_db
179
+ SECRET_KEY=your-secret-key-here
180
+ DEBUG=true
181
+ ALLOWED_HOSTS=localhost,127.0.0.1
182
+ ```
183
+
184
+ ## 📄 License
185
+
186
+ MIT
@@ -0,0 +1,38 @@
1
+ src/__init__.py,sha256=oQrsYQeSZ9rrr0VQBJO2mgZQF1LoYwqDzJ3Y2eqQLUk,322
2
+ src/config.py,sha256=VcygAomUdTpU00T6cNf-enIL5IK2y6460FmTJsOlvZo,2698
3
+ src/main.py,sha256=2Bfb5tT2YfWcQgJgIBDN33uQ__SV7GPS-V7RgFqrgqk,3749
4
+ src/application/__init__.py,sha256=ZElJDu9pRzpuDW0Do4_nYYawfgGx9iaw7eEfgSmPe1s,51
5
+ src/application/services/__init__.py,sha256=aYwIt4J1V0C4ZOV7lThad-m-RfUq-P04DCC0Z09j7nE,65
6
+ src/application/services/auth_service.py,sha256=pmv1oWocS5HOyOeZ3QoIXBrkjC2-ijqGftZFpAs9Shc,6005
7
+ src/application/services/order_service.py,sha256=E7sdX6I0ZNpM5ME_9l6gFv_K173a0Rx7uie952A5ls0,8838
8
+ src/domain/__init__.py,sha256=ypBUHIJZruIYfQBV1AcUh61nzXaD51pmvVQN3Yb4Xq4,60
9
+ src/domain/entities/__init__.py,sha256=GtF8iOAgiGQLWlnWacT2oeVos0ZjqoGSPJLalSYiCW8,482
10
+ src/domain/entities/inventory.py,sha256=BH37B19HR_kk49vBIS6pL4OUGGB9jomEYveKvQTjYwU,9017
11
+ src/domain/entities/order.py,sha256=38F703Z9zSfn_TD3nfK610YFa2y2oYqK2OviwerM38c,9969
12
+ src/domain/entities/organization.py,sha256=vUM1KeP9pZSVkxrpyOVP2wBzEBgoBRrggM1C9vlDoHU,4695
13
+ src/domain/entities/user.py,sha256=waXVxbrD3ct2fHSCdlmjxXAbRHFOFPUS7Qi5ARnXr5A,5067
14
+ src/domain/value_objects/__init__.py,sha256=0_paVmj4nrpMvsTl2UVH4uHYARUTPBdvqPw92FNjtHU,265
15
+ src/domain/value_objects/address.py,sha256=Mv9Y_PVy8-MhEIRIebRZf7PYT70hnYPhh3HJgP6vX0E,3167
16
+ src/domain/value_objects/email.py,sha256=NWUQmGaw-NPlDdURG-fSbcXFb9nUS1NHE-K30MJ0ueA,2695
17
+ src/domain/value_objects/money.py,sha256=X6xPCcc-wgiQFSAkRqOWscdXl2qYcQG0bTaimlCLeB8,6865
18
+ src/infrastructure/__init__.py,sha256=4LA_7dRyRn4yazu_gEsHqtwZRNN7TXjb2eMZvQFCEuc,72
19
+ src/infrastructure/database/__init__.py,sha256=tPggXY8A-AMe-eaHKCp9GjJoaWcYod05e1kJgOkJB54,68
20
+ src/infrastructure/database/models.py,sha256=DLyf8qy6uHNQZCw9ywRbyljH0z5vcIRXNxatN2I6Y6U,11511
21
+ src/infrastructure/database/session.py,sha256=KYfSMLtYzeJu6v9KCVAziNk0otrPWYiF9BWmXbASKdA,2454
22
+ src/infrastructure/repositories/__init__.py,sha256=QUeE09uMy99i-rw3M7jnNqc_xmnG-Hf_0RSuFtgbHlA,55
23
+ src/infrastructure/repositories/base.py,sha256=hG0V9rDMj8jiBSpabg1W2feTBQ2MesXOZypbQ9SXmfA,4558
24
+ src/infrastructure/repositories/inventory_repository.py,sha256=SC3dlndgvYJdVTce0TIHB5sBRM3U7jBIZM2BwQKoPHA,8682
25
+ src/infrastructure/repositories/order_repository.py,sha256=iMzSQ816TRl0zBBIOSELI5Vlr_AvxY_3BMQ7kyVYgb0,8043
26
+ src/infrastructure/repositories/user_repository.py,sha256=g8Rw2fytdRoGc4QajskvmAt4cy9siwQ_TiUBSU5e3U0,4146
27
+ src/presentation/__init__.py,sha256=_JdpNelTs5YHmj2KokTDS_wRjbqVpwA-FHoNOsf9msM,52
28
+ src/presentation/routes/__init__.py,sha256=m4rQ8jwQMOqgZDrGnTmvbpKI4SlIGvFid01yyq3P5eY,26
29
+ src/presentation/routes/auth.py,sha256=CnpF0spaLWiXOCNOYsW5kzo9zDlu1UKuJhLUL-6bxGo,6079
30
+ src/presentation/routes/health.py,sha256=BqcCK_S9KVkpK2RmkG4ADiOOLNruFNTTHOGk4GAm3b8,2521
31
+ src/presentation/routes/inventory.py,sha256=COk5BloU0gPFnacq4PjeIkbKPWhcZOeYTeRDmD1RP1k,10346
32
+ src/presentation/routes/orders.py,sha256=BpOzvyVeG09chzjGmw0gHZ2jEbKHPNSTt460KYVF_G0,9508
33
+ src/presentation/routes/users.py,sha256=gEbhflqwP7sZdNztadSTQHRBPU3cj7fjVMQWxOOyJJw,7266
34
+ src/presentation/schemas/__init__.py,sha256=LosHFlWnOB7DnjS1z07aPcbUuVNSfH_6aktob8u5y_k,53
35
+ backend_systems-1.0.0.dist-info/METADATA,sha256=0KmS_pyWBGmm9ZbSJO6S8Zcxc0jy7M1KJX-tkF5pjOI,6898
36
+ backend_systems-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
37
+ backend_systems-1.0.0.dist-info/top_level.txt,sha256=74rtVfumQlgAPzR5_2CgYN24MB0XARCg0t-gzk6gTrM,4
38
+ backend_systems-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ src
src/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ """
2
+ Backend Systems - ERP-Style Backend with DDD and Clean Architecture.
3
+
4
+ This package provides a production-grade backend implementation demonstrating
5
+ enterprise patterns including Domain-Driven Design, CQRS-ready architecture,
6
+ and comprehensive RBAC.
7
+ """
8
+
9
+ __version__ = "1.0.0"
10
+ __author__ = "Engineering Team"
@@ -0,0 +1 @@
1
+ """Application layer - Use cases and services."""
@@ -0,0 +1 @@
1
+ """Application services - Business use case implementations."""
@@ -0,0 +1,216 @@
1
+ """
2
+ Authentication Service - Handles user authentication and token management.
3
+ """
4
+
5
+ from datetime import datetime, timedelta
6
+ from typing import Optional
7
+ from uuid import UUID
8
+
9
+ from jose import JWTError, jwt
10
+ from passlib.context import CryptContext
11
+
12
+ from src.config import get_settings
13
+ from src.domain.entities.user import User
14
+
15
+
16
+ class AuthenticationService:
17
+ """
18
+ Service for handling authentication operations.
19
+
20
+ Manages password hashing, verification, and JWT token generation.
21
+ """
22
+
23
+ ALGORITHM = "HS256"
24
+
25
+ def __init__(self):
26
+ """Initialize authentication service."""
27
+ self._settings = get_settings()
28
+ self._pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
29
+
30
+ def hash_password(self, password: str) -> str:
31
+ """
32
+ Hash a plain text password.
33
+
34
+ Args:
35
+ password: Plain text password
36
+
37
+ Returns:
38
+ Hashed password string
39
+ """
40
+ return self._pwd_context.hash(password)
41
+
42
+ def verify_password(self, plain_password: str, hashed_password: str) -> bool:
43
+ """
44
+ Verify a password against its hash.
45
+
46
+ Args:
47
+ plain_password: Plain text password to verify
48
+ hashed_password: Stored password hash
49
+
50
+ Returns:
51
+ True if password matches, False otherwise
52
+ """
53
+ return self._pwd_context.verify(plain_password, hashed_password)
54
+
55
+ def create_access_token(
56
+ self,
57
+ user_id: UUID,
58
+ email: str,
59
+ role: str,
60
+ expires_delta: Optional[timedelta] = None,
61
+ ) -> str:
62
+ """
63
+ Create a JWT access token.
64
+
65
+ Args:
66
+ user_id: User identifier
67
+ email: User email
68
+ role: User role
69
+ expires_delta: Optional custom expiration
70
+
71
+ Returns:
72
+ Encoded JWT token
73
+ """
74
+ if expires_delta is None:
75
+ expires_delta = timedelta(minutes=self._settings.access_token_expire_minutes)
76
+
77
+ expire = datetime.utcnow() + expires_delta
78
+
79
+ to_encode = {
80
+ "sub": str(user_id),
81
+ "email": email,
82
+ "role": role,
83
+ "exp": expire,
84
+ "type": "access",
85
+ }
86
+
87
+ return jwt.encode(
88
+ to_encode,
89
+ self._settings.secret_key,
90
+ algorithm=self.ALGORITHM,
91
+ )
92
+
93
+ def create_refresh_token(
94
+ self,
95
+ user_id: UUID,
96
+ expires_delta: Optional[timedelta] = None,
97
+ ) -> str:
98
+ """
99
+ Create a JWT refresh token.
100
+
101
+ Args:
102
+ user_id: User identifier
103
+ expires_delta: Optional custom expiration
104
+
105
+ Returns:
106
+ Encoded JWT refresh token
107
+ """
108
+ if expires_delta is None:
109
+ expires_delta = timedelta(days=self._settings.refresh_token_expire_days)
110
+
111
+ expire = datetime.utcnow() + expires_delta
112
+
113
+ to_encode = {
114
+ "sub": str(user_id),
115
+ "exp": expire,
116
+ "type": "refresh",
117
+ }
118
+
119
+ return jwt.encode(
120
+ to_encode,
121
+ self._settings.secret_key,
122
+ algorithm=self.ALGORITHM,
123
+ )
124
+
125
+ def decode_token(self, token: str) -> Optional[dict]:
126
+ """
127
+ Decode and validate a JWT token.
128
+
129
+ Args:
130
+ token: JWT token string
131
+
132
+ Returns:
133
+ Token payload if valid, None otherwise
134
+ """
135
+ try:
136
+ payload = jwt.decode(
137
+ token,
138
+ self._settings.secret_key,
139
+ algorithms=[self.ALGORITHM],
140
+ )
141
+ return payload
142
+ except JWTError:
143
+ return None
144
+
145
+ def validate_access_token(self, token: str) -> Optional[dict]:
146
+ """
147
+ Validate an access token.
148
+
149
+ Args:
150
+ token: JWT access token
151
+
152
+ Returns:
153
+ Token payload if valid access token, None otherwise
154
+ """
155
+ payload = self.decode_token(token)
156
+ if payload and payload.get("type") == "access":
157
+ return payload
158
+ return None
159
+
160
+ def validate_refresh_token(self, token: str) -> Optional[str]:
161
+ """
162
+ Validate a refresh token.
163
+
164
+ Args:
165
+ token: JWT refresh token
166
+
167
+ Returns:
168
+ User ID if valid refresh token, None otherwise
169
+ """
170
+ payload = self.decode_token(token)
171
+ if payload and payload.get("type") == "refresh":
172
+ return payload.get("sub")
173
+ return None
174
+
175
+ def authenticate_user(
176
+ self,
177
+ user: User,
178
+ password: str,
179
+ ) -> bool:
180
+ """
181
+ Authenticate a user with password.
182
+
183
+ Args:
184
+ user: User entity
185
+ password: Plain text password
186
+
187
+ Returns:
188
+ True if authentication successful
189
+
190
+ Raises:
191
+ ValueError: If account is locked or inactive
192
+ """
193
+ if not user.is_active:
194
+ raise ValueError("Account is inactive")
195
+
196
+ if user.is_locked():
197
+ raise ValueError("Account is temporarily locked")
198
+
199
+ if not self.verify_password(password, user.hashed_password):
200
+ user.record_failed_login()
201
+ return False
202
+
203
+ user.record_successful_login()
204
+ return True
205
+
206
+
207
+ # Singleton instance
208
+ _auth_service: Optional[AuthenticationService] = None
209
+
210
+
211
+ def get_auth_service() -> AuthenticationService:
212
+ """Get authentication service singleton."""
213
+ global _auth_service
214
+ if _auth_service is None:
215
+ _auth_service = AuthenticationService()
216
+ return _auth_service