ntc-dev-kit 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 (148) hide show
  1. ntc/__init__.py +5 -0
  2. ntc/app_creator.py +296 -0
  3. ntc/cli.py +4 -0
  4. ntc/cli_enhanced.py +820 -0
  5. ntc/composer/__init__.py +0 -0
  6. ntc/composer/main.py +443 -0
  7. ntc/deployment/__init__.py +0 -0
  8. ntc/templates/__init__.py +2 -0
  9. ntc/templates/auth_app/__init__.py +0 -0
  10. ntc/templates/auth_app/admin.py +24 -0
  11. ntc/templates/auth_app/apps.py +6 -0
  12. ntc/templates/auth_app/models.py +30 -0
  13. ntc/templates/auth_app/serializers.py +101 -0
  14. ntc/templates/auth_app/urls.py +19 -0
  15. ntc/templates/auth_app/views.py +175 -0
  16. ntc/templates/drf_structure/__init__.py +1 -0
  17. ntc/templates/drf_structure/drf_structure.md +83 -0
  18. ntc/templates/drf_structure/main.py +389 -0
  19. ntc/templates/drf_structure/structure.json +126 -0
  20. ntc/templates/drf_structure/templates/.entry/start_core_backend.sh +37 -0
  21. ntc/templates/drf_structure/templates/.entry/start_tasks_backend.sh +30 -0
  22. ntc/templates/drf_structure/templates/CLAUDE.md +194 -0
  23. ntc/templates/drf_structure/templates/README.md +117 -0
  24. ntc/templates/drf_structure/templates/apps/app_admin.py +3 -0
  25. ntc/templates/drf_structure/templates/apps/app_apps.py +6 -0
  26. ntc/templates/drf_structure/templates/apps/app_models.py +4 -0
  27. ntc/templates/drf_structure/templates/apps/app_urls.py +8 -0
  28. ntc/templates/drf_structure/templates/apps/app_views.py +6 -0
  29. ntc/templates/drf_structure/templates/apps/common_apps.py +6 -0
  30. ntc/templates/drf_structure/templates/apps/common_models.py +8 -0
  31. ntc/templates/drf_structure/templates/config/gunicorn_config.py +9 -0
  32. ntc/templates/drf_structure/templates/config/http.conf +33 -0
  33. ntc/templates/drf_structure/templates/core/base_model.py +9 -0
  34. ntc/templates/drf_structure/templates/core/formatter.py +5 -0
  35. ntc/templates/drf_structure/templates/core/logger.py +8 -0
  36. ntc/templates/drf_structure/templates/core/pagination.py +6 -0
  37. ntc/templates/drf_structure/templates/core/response_wrapper.py +7 -0
  38. ntc/templates/drf_structure/templates/core/services/azure/email/backend.py +108 -0
  39. ntc/templates/drf_structure/templates/core/services/azure/email/client.py +296 -0
  40. ntc/templates/drf_structure/templates/core/services/azure/email/notifier.py +102 -0
  41. ntc/templates/drf_structure/templates/core/services/object_storage.py +285 -0
  42. ntc/templates/drf_structure/templates/django_project/asgi.py +5 -0
  43. ntc/templates/drf_structure/templates/django_project/celery.py +8 -0
  44. ntc/templates/drf_structure/templates/django_project/dev_settings.py +2 -0
  45. ntc/templates/drf_structure/templates/django_project/django_settings.txt +322 -0
  46. ntc/templates/drf_structure/templates/django_project/management_command.py +114 -0
  47. ntc/templates/drf_structure/templates/django_project/prod_settings.py +3 -0
  48. ntc/templates/drf_structure/templates/django_project/schema.py +7 -0
  49. ntc/templates/drf_structure/templates/django_project/urls.py +6 -0
  50. ntc/templates/drf_structure/templates/django_project/wsgi.py +5 -0
  51. ntc/templates/drf_structure/templates/docker/docker-compose.yml +11 -0
  52. ntc/templates/drf_structure/templates/knowledge/knowledge_description.md +201 -0
  53. ntc/templates/drf_structure/templates/knowledge/knowledge_design_pattern.md +722 -0
  54. ntc/templates/drf_structure/templates/knowledge/knowledge_structure.md +151 -0
  55. ntc/templates/drf_structure/templates/root/README.md +307 -0
  56. ntc/templates/drf_structure/templates/root/django_env.txt +98 -0
  57. ntc/templates/drf_structure/templates/root/django_requirements.txt +24 -0
  58. ntc/templates/drf_structure/templates/root/gitignore.txt +6 -0
  59. ntc/templates/drf_structure/templates/root/manage.py +11 -0
  60. ntc/templates/flutter/.gitignore +44 -0
  61. ntc/templates/flutter/.knowledge/knowledge_description.md +215 -0
  62. ntc/templates/flutter/.knowledge/knowledge_design_pattern.md +310 -0
  63. ntc/templates/flutter/.knowledge/knowledge_structure.md +83 -0
  64. ntc/templates/flutter/CLAUDE.md +190 -0
  65. ntc/templates/flutter/__init__.py +0 -0
  66. ntc/templates/flutter/analysis_options.yaml +9 -0
  67. ntc/templates/flutter/main.py +187 -0
  68. ntc/templates/flutter/pubspec.yaml +65 -0
  69. ntc/templates/flutter/structure.json +51 -0
  70. ntc/templates/nextjs/.env.example +10 -0
  71. ntc/templates/nextjs/.gitignore +36 -0
  72. ntc/templates/nextjs/CLAUDE.md +181 -0
  73. ntc/templates/nextjs/README.md +94 -0
  74. ntc/templates/nextjs/__init__.py +0 -0
  75. ntc/templates/nextjs/knowledge/knowledge_description.md +208 -0
  76. ntc/templates/nextjs/knowledge/knowledge_design_pattern.md +460 -0
  77. ntc/templates/nextjs/knowledge/knowledge_structure.md +75 -0
  78. ntc/templates/nextjs/main.py +224 -0
  79. ntc/templates/nextjs/package.json +25 -0
  80. ntc/templates/nextjs/src/app/globals.css +26 -0
  81. ntc/templates/nextjs/src/app/layout.tsx +59 -0
  82. ntc/templates/nextjs/src/app/page.tsx +21 -0
  83. ntc/templates/nextjs/src/components/Header.tsx +16 -0
  84. ntc/templates/nextjs/src/services/api.ts +81 -0
  85. ntc/templates/nextjs/src/utils/constants.ts +1 -0
  86. ntc/templates/nextjs/structure.json +32 -0
  87. ntc/templates/nextjs/tsconfig.json +27 -0
  88. ntc/templates/react_admin/.env.example +1 -0
  89. ntc/templates/react_admin/.gitignore +10 -0
  90. ntc/templates/react_admin/CLAUDE.md +210 -0
  91. ntc/templates/react_admin/README.md +72 -0
  92. ntc/templates/react_admin/__init__.py +0 -0
  93. ntc/templates/react_admin/index.html +13 -0
  94. ntc/templates/react_admin/knowledge/knowledge_description.md +202 -0
  95. ntc/templates/react_admin/knowledge/knowledge_design_pattern.md +448 -0
  96. ntc/templates/react_admin/knowledge/knowledge_structure.md +97 -0
  97. ntc/templates/react_admin/main.py +208 -0
  98. ntc/templates/react_admin/package.json +35 -0
  99. ntc/templates/react_admin/src/App.tsx +60 -0
  100. ntc/templates/react_admin/src/components/ProtectedRoute.tsx +16 -0
  101. ntc/templates/react_admin/src/components/Sidebar.tsx +90 -0
  102. ntc/templates/react_admin/src/components/TopNav.tsx +44 -0
  103. ntc/templates/react_admin/src/context/AuthContext.tsx +107 -0
  104. ntc/templates/react_admin/src/context/ThemeContext.tsx +34 -0
  105. ntc/templates/react_admin/src/context/WorkspaceContext.tsx +28 -0
  106. ntc/templates/react_admin/src/hooks/useApi.ts +39 -0
  107. ntc/templates/react_admin/src/index.css +34 -0
  108. ntc/templates/react_admin/src/layouts/AuthLayout.tsx +23 -0
  109. ntc/templates/react_admin/src/layouts/DashboardLayout.tsx +24 -0
  110. ntc/templates/react_admin/src/main.tsx +5 -0
  111. ntc/templates/react_admin/src/pages/DashboardPage.tsx +10 -0
  112. ntc/templates/react_admin/src/pages/auth/LoginPage.tsx +79 -0
  113. ntc/templates/react_admin/src/pages/auth/RegisterPage.tsx +93 -0
  114. ntc/templates/react_admin/src/services/api.service.ts +44 -0
  115. ntc/templates/react_admin/src/services/base.service.ts +193 -0
  116. ntc/templates/react_admin/structure.json +52 -0
  117. ntc/templates/react_admin/tsconfig.app.json +25 -0
  118. ntc/templates/react_admin/tsconfig.json +7 -0
  119. ntc/templates/react_admin/tsconfig.node.json +19 -0
  120. ntc/templates/react_admin/vite.config.ts +13 -0
  121. ntc/templates/react_structure/CLAUDE.md +176 -0
  122. ntc/templates/react_structure/__init__.py +1 -0
  123. ntc/templates/react_structure/index.css +21 -0
  124. ntc/templates/react_structure/index.html +13 -0
  125. ntc/templates/react_structure/knowledge/knowledge_description.md +170 -0
  126. ntc/templates/react_structure/knowledge/knowledge_design_pattern.md +411 -0
  127. ntc/templates/react_structure/knowledge/knowledge_structure.md +74 -0
  128. ntc/templates/react_structure/main.py +217 -0
  129. ntc/templates/react_structure/package.json +32 -0
  130. ntc/templates/react_structure/src/App.tsx +63 -0
  131. ntc/templates/react_structure/src/components/Header.tsx +51 -0
  132. ntc/templates/react_structure/src/main.tsx +5 -0
  133. ntc/templates/react_structure/src/services/api.ts +90 -0
  134. ntc/templates/react_structure/src/utils/constants.ts +1 -0
  135. ntc/templates/react_structure/structure.json +65 -0
  136. ntc/templates/react_structure/tsconfig.app.json +25 -0
  137. ntc/templates/react_structure/tsconfig.json +7 -0
  138. ntc/templates/react_structure/tsconfig.node.json +19 -0
  139. ntc/templates/react_structure/vite.config.ts +12 -0
  140. ntc/utils/__init__.py +1 -0
  141. ntc/utils/environment.py +67 -0
  142. ntc/utils/output.py +41 -0
  143. ntc_dev_kit-1.0.0.dist-info/METADATA +188 -0
  144. ntc_dev_kit-1.0.0.dist-info/RECORD +148 -0
  145. ntc_dev_kit-1.0.0.dist-info/WHEEL +5 -0
  146. ntc_dev_kit-1.0.0.dist-info/entry_points.txt +2 -0
  147. ntc_dev_kit-1.0.0.dist-info/licenses/LICENSE +21 -0
  148. ntc_dev_kit-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,722 @@
1
+ # Design Patterns - {project_name}
2
+
3
+ This document outlines the design patterns, architectural decisions, and coding conventions used in this NTC-generated Django REST Framework project.
4
+
5
+ ## 🏗️ Architectural Patterns
6
+
7
+ ### **1. Service/Selector Pattern**
8
+
9
+ **Purpose**: Separate business logic from views and data access logic.
10
+
11
+ **Structure**:
12
+ ```
13
+ apps/myapp/
14
+ ├── views.py # HTTP request/response handling only
15
+ ├── services/ # Business logic and operations
16
+ ├── selectors/ # Data querying and filtering
17
+ └── serializers/ # Data validation and transformation
18
+ ```
19
+
20
+ **Example Implementation**:
21
+
22
+ ```python
23
+ # selectors/user_selector.py
24
+ from django.db.models import QuerySet
25
+ from apps.users.models import User
26
+
27
+ def get_active_users() -> QuerySet[User]:
28
+ """Get all active users."""
29
+ return User.objects.filter(is_active=True)
30
+
31
+ def get_user_by_email(email: str) -> User:
32
+ """Get user by email address."""
33
+ return User.objects.get(email=email)
34
+
35
+ # services/user_service.py
36
+ from django.contrib.auth import authenticate
37
+ from apps.users.models import User
38
+ from apps.users.selectors.user_selector import get_user_by_email
39
+
40
+ def create_user(email: str, password: str, **kwargs) -> User:
41
+ """Create a new user with validation."""
42
+ if User.objects.filter(email=email).exists():
43
+ raise ValueError("User with this email already exists")
44
+
45
+ user = User.objects.create_user(
46
+ email=email,
47
+ password=password,
48
+ **kwargs
49
+ )
50
+ return user
51
+
52
+ def authenticate_user(email: str, password: str) -> User:
53
+ """Authenticate user credentials."""
54
+ user = authenticate(email=email, password=password)
55
+ if not user:
56
+ raise ValueError("Invalid credentials")
57
+ return user
58
+
59
+ # views.py
60
+ from rest_framework.decorators import api_view
61
+ from core.response_wrapper import success_response, error_response
62
+ from .services.user_service import create_user, authenticate_user
63
+ from .selectors.user_selector import get_active_users
64
+
65
+ @api_view(['POST'])
66
+ def create_user_view(request):
67
+ """Create a new user."""
68
+ try:
69
+ user = create_user(
70
+ email=request.data['email'],
71
+ password=request.data['password']
72
+ )
73
+ return success_response(
74
+ data={"user_id": user.id},
75
+ message="User created successfully"
76
+ )
77
+ except ValueError as e:
78
+ return error_response(message=str(e), code=400)
79
+ ```
80
+
81
+ ### **2. Repository Pattern (via Selectors)**
82
+
83
+ **Purpose**: Abstract data access logic and provide a consistent interface for querying data.
84
+
85
+ ```python
86
+ # selectors/base_selector.py
87
+ from typing import Optional, List
88
+ from django.db.models import Model, QuerySet
89
+
90
+ class BaseSelector:
91
+ model = None
92
+
93
+ @classmethod
94
+ def get_by_id(cls, id: int) -> Optional[Model]:
95
+ """Get object by ID."""
96
+ try:
97
+ return cls.model.objects.get(id=id)
98
+ except cls.model.DoesNotExist:
99
+ return None
100
+
101
+ @classmethod
102
+ def get_all(cls) -> QuerySet:
103
+ """Get all objects."""
104
+ return cls.model.objects.all()
105
+
106
+ @classmethod
107
+ def filter_by(cls, **kwargs) -> QuerySet:
108
+ """Filter objects by given criteria."""
109
+ return cls.model.objects.filter(**kwargs)
110
+
111
+ # selectors/user_selector.py
112
+ from .base_selector import BaseSelector
113
+ from apps.users.models import User
114
+
115
+ class UserSelector(BaseSelector):
116
+ model = User
117
+
118
+ @classmethod
119
+ def get_active_users(cls) -> QuerySet:
120
+ """Get all active users."""
121
+ return cls.filter_by(is_active=True)
122
+ ```
123
+
124
+ ### **3. Factory Pattern (for Model Creation)**
125
+
126
+ **Purpose**: Centralize object creation logic with proper validation.
127
+
128
+ ```python
129
+ # services/factories/user_factory.py
130
+ from apps.users.models import User
131
+ from apps.common.models import TimeStampedModel
132
+
133
+ class UserFactory:
134
+ @staticmethod
135
+ def create_user(email: str, password: str, **kwargs) -> User:
136
+ """Create user with proper validation."""
137
+ # Validation logic
138
+ if not email:
139
+ raise ValueError("Email is required")
140
+
141
+ # Creation logic
142
+ user = User.objects.create_user(
143
+ email=email,
144
+ password=password,
145
+ **kwargs
146
+ )
147
+
148
+ # Post-creation logic (send welcome email, etc.)
149
+ # ...
150
+
151
+ return user
152
+ ```
153
+
154
+ ## 🎯 Response Pattern
155
+
156
+ ### **Standardized API Responses**
157
+
158
+ **Purpose**: Consistent response format across all endpoints.
159
+
160
+ ```python
161
+ # core/response_wrapper.py
162
+ from rest_framework.response import Response
163
+
164
+ def success_response(data=None, message='Success', status=200):
165
+ """Standard success response format."""
166
+ return Response({
167
+ 'status': 'success',
168
+ 'message': message,
169
+ 'data': data
170
+ }, status=status)
171
+
172
+ def error_response(message='Error', code=400, errors=None):
173
+ """Standard error response format."""
174
+ response_data = {
175
+ 'status': 'error',
176
+ 'message': message
177
+ }
178
+ if errors:
179
+ response_data['errors'] = errors
180
+
181
+ return Response(response_data, status=code)
182
+
183
+ # Usage in views
184
+ @api_view(['GET'])
185
+ def get_users(request):
186
+ users = get_active_users()
187
+ serializer = UserSerializer(users, many=True)
188
+ return success_response(
189
+ data=serializer.data,
190
+ message="Users retrieved successfully"
191
+ )
192
+ ```
193
+
194
+ ## 🗄️ Model Patterns
195
+
196
+ ### **1. Abstract Base Models**
197
+
198
+ **Purpose**: Provide common fields and functionality across models.
199
+
200
+ ```python
201
+ # apps/common/models.py
202
+ from django.db import models
203
+
204
+ class TimeStampedModel(models.Model):
205
+ """Abstract model with timestamp fields."""
206
+ created_at = models.DateTimeField(auto_now_add=True)
207
+ updated_at = models.DateTimeField(auto_now=True)
208
+
209
+ class Meta:
210
+ abstract = True
211
+
212
+ # core/base_model.py
213
+ from django.db import models
214
+
215
+ class BaseModel(models.Model):
216
+ """Enhanced base model with common fields."""
217
+ id = models.BigAutoField(primary_key=True)
218
+ created_at = models.DateTimeField(auto_now_add=True)
219
+ updated_at = models.DateTimeField(auto_now=True)
220
+
221
+ class Meta:
222
+ abstract = True
223
+
224
+ def __str__(self):
225
+ return f"{self.__class__.__name__}({self.id})"
226
+
227
+ # Usage in app models
228
+ from apps.common.models import TimeStampedModel
229
+
230
+ class User(TimeStampedModel):
231
+ email = models.EmailField(unique=True)
232
+ first_name = models.CharField(max_length=100)
233
+ # ... other fields
234
+ ```
235
+
236
+ ### **2. Manager Pattern**
237
+
238
+ **Purpose**: Custom query methods at the model level.
239
+
240
+ ```python
241
+ # apps/users/models.py
242
+ from django.contrib.auth.models import AbstractUser
243
+ from django.db import models
244
+
245
+ class ActiveUserManager(models.Manager):
246
+ """Manager for active users only."""
247
+ def get_queryset(self):
248
+ return super().get_queryset().filter(is_active=True)
249
+
250
+ class User(AbstractUser):
251
+ email = models.EmailField(unique=True)
252
+
253
+ # Managers
254
+ objects = models.Manager() # Default manager
255
+ active = ActiveUserManager() # Custom manager
256
+
257
+ USERNAME_FIELD = 'email'
258
+ REQUIRED_FIELDS = []
259
+
260
+ # Usage
261
+ active_users = User.active.all() # Only active users
262
+ all_users = User.objects.all() # All users
263
+ ```
264
+
265
+ ## 🔐 Authentication Patterns
266
+
267
+ ### **JWT Token Pattern**
268
+
269
+ **Purpose**: Stateless authentication with refresh token support.
270
+
271
+ ```python
272
+ # services/auth_service.py
273
+ from rest_framework_simplejwt.tokens import RefreshToken
274
+ from django.contrib.auth import authenticate
275
+
276
+ class AuthService:
277
+ @staticmethod
278
+ def login_user(email: str, password: str) -> dict:
279
+ """Authenticate user and return tokens."""
280
+ user = authenticate(email=email, password=password)
281
+ if not user:
282
+ raise ValueError("Invalid credentials")
283
+
284
+ refresh = RefreshToken.for_user(user)
285
+ return {
286
+ 'refresh': str(refresh),
287
+ 'access': str(refresh.access_token),
288
+ 'user': {
289
+ 'id': user.id,
290
+ 'email': user.email,
291
+ 'first_name': user.first_name,
292
+ }
293
+ }
294
+
295
+ @staticmethod
296
+ def refresh_token(refresh_token: str) -> dict:
297
+ """Refresh access token."""
298
+ try:
299
+ refresh = RefreshToken(refresh_token)
300
+ return {
301
+ 'access': str(refresh.access_token)
302
+ }
303
+ except Exception:
304
+ raise ValueError("Invalid refresh token")
305
+ ```
306
+
307
+ ## 📊 Serializer Patterns
308
+
309
+ ### **Nested Serializer Pattern**
310
+
311
+ **Purpose**: Handle complex data relationships efficiently.
312
+
313
+ ```python
314
+ # serializers/user_serializer.py
315
+ from rest_framework import serializers
316
+ from apps.users.models import User, Profile
317
+
318
+ class ProfileSerializer(serializers.ModelSerializer):
319
+ class Meta:
320
+ model = Profile
321
+ fields = ['bio', 'avatar', 'phone']
322
+
323
+ class UserSerializer(serializers.ModelSerializer):
324
+ profile = ProfileSerializer(read_only=True)
325
+ full_name = serializers.SerializerMethodField()
326
+
327
+ class Meta:
328
+ model = User
329
+ fields = ['id', 'email', 'first_name', 'last_name', 'full_name', 'profile']
330
+
331
+ def get_full_name(self, obj):
332
+ return f"{obj.first_name} {obj.last_name}".strip()
333
+
334
+ class UserCreateSerializer(serializers.ModelSerializer):
335
+ password = serializers.CharField(write_only=True)
336
+
337
+ class Meta:
338
+ model = User
339
+ fields = ['email', 'first_name', 'last_name', 'password']
340
+
341
+ def create(self, validated_data):
342
+ password = validated_data.pop('password')
343
+ user = User.objects.create_user(password=password, **validated_data)
344
+ return user
345
+ ```
346
+
347
+ ## 🔄 Task Patterns (Celery)
348
+
349
+ ### **Background Task Pattern**
350
+
351
+ **Purpose**: Handle long-running operations asynchronously.
352
+
353
+ ```python
354
+ # tasks/email_tasks.py
355
+ from celery import shared_task
356
+ from django.core.mail import send_mail
357
+ from django.conf import settings
358
+
359
+ @shared_task
360
+ def send_welcome_email(user_id: int):
361
+ """Send welcome email to new user."""
362
+ from apps.users.models import User
363
+
364
+ try:
365
+ user = User.objects.get(id=user_id)
366
+ send_mail(
367
+ subject='Welcome to {project_name}',
368
+ message=f'Welcome {user.first_name}!',
369
+ from_email=settings.DEFAULT_FROM_EMAIL,
370
+ recipient_list=[user.email],
371
+ )
372
+ return f"Welcome email sent to {user.email}"
373
+ except User.DoesNotExist:
374
+ return f"User with ID {user_id} not found"
375
+
376
+ # Usage in services
377
+ from .tasks.email_tasks import send_welcome_email
378
+
379
+ def create_user(email: str, password: str) -> User:
380
+ user = User.objects.create_user(email=email, password=password)
381
+
382
+ # Send welcome email asynchronously
383
+ send_welcome_email.delay(user.id)
384
+
385
+ return user
386
+ ```
387
+
388
+ ## 🧪 Testing Patterns
389
+
390
+ ### **Test Organization Pattern**
391
+
392
+ **Purpose**: Structured testing approach with proper fixtures.
393
+
394
+ ```python
395
+ # tests/test_user_service.py
396
+ from django.test import TestCase
397
+ from apps.users.services.user_service import create_user, authenticate_user
398
+ from apps.users.models import User
399
+
400
+ class UserServiceTest(TestCase):
401
+ def setUp(self):
402
+ """Set up test data."""
403
+ self.user_data = {
404
+ 'email': 'test@example.com',
405
+ 'password': 'testpass123',
406
+ 'first_name': 'Test'
407
+ }
408
+
409
+ def test_create_user_success(self):
410
+ """Test successful user creation."""
411
+ user = create_user(**self.user_data)
412
+
413
+ self.assertIsInstance(user, User)
414
+ self.assertEqual(user.email, self.user_data['email'])
415
+ self.assertTrue(user.check_password(self.user_data['password']))
416
+
417
+ def test_create_user_duplicate_email(self):
418
+ """Test user creation with duplicate email."""
419
+ create_user(**self.user_data)
420
+
421
+ with self.assertRaises(ValueError):
422
+ create_user(**self.user_data)
423
+ ```
424
+
425
+ ## 📋 Naming Conventions
426
+
427
+ ### **File and Directory Naming**
428
+ - **Apps**: `snake_case` (e.g., `user_management`)
429
+ - **Models**: `PascalCase` (e.g., `UserProfile`)
430
+ - **Views**: `snake_case` functions (e.g., `create_user_view`)
431
+ - **Services**: `snake_case` functions (e.g., `create_user`)
432
+ - **URLs**: `kebab-case` (e.g., `/api/user-profile/`)
433
+
434
+ ### **Variable Naming**
435
+ - **Python**: `snake_case`
436
+ - **Constants**: `UPPER_SNAKE_CASE`
437
+ - **Private methods**: `_leading_underscore`
438
+ - **Class methods**: `snake_case`
439
+
440
+ ## 🎯 Error Handling Patterns
441
+
442
+ ### **Centralized Error Handling**
443
+
444
+ ```python
445
+ # core/exceptions.py
446
+ class BusinessLogicError(Exception):
447
+ """Custom exception for business logic errors."""
448
+ def __init__(self, message, code=400):
449
+ self.message = message
450
+ self.code = code
451
+ super().__init__(self.message)
452
+
453
+ # services/base_service.py
454
+ from core.exceptions import BusinessLogicError
455
+
456
+ class BaseService:
457
+ @staticmethod
458
+ def validate_required_fields(data: dict, required_fields: list):
459
+ """Validate required fields in data."""
460
+ missing_fields = [field for field in required_fields if not data.get(field)]
461
+ if missing_fields:
462
+ raise BusinessLogicError(
463
+ f"Missing required fields: {', '.join(missing_fields)}"
464
+ )
465
+ ```
466
+
467
+ ## 🌐 External Service Patterns
468
+
469
+ ### **Azure Communication Services Email Pattern**
470
+
471
+ **Purpose**: Send emails through Azure Communication Services with template support.
472
+
473
+ ```python
474
+ # core/services/azure/email/notifier.py
475
+ from core.services.azure.email import send_template_email
476
+
477
+ # Send templated email
478
+ send_template_email(
479
+ subject="Welcome to {project_name}",
480
+ to="user@example.com",
481
+ template="welcome_email", # Looks for welcome_email.html and welcome_email.txt
482
+ context={"name": "John", "verification_url": "https://..."},
483
+ sender="notification", # or "no_reply", "support", "marketing"
484
+ fail_silently=False
485
+ )
486
+ ```
487
+
488
+ **Template Structure**:
489
+ ```
490
+ templates/
491
+ ├── welcome_email.html # Required HTML template
492
+ └── welcome_email.txt # Optional plain text template (auto-generated if missing)
493
+ ```
494
+
495
+ **Sender Configuration**:
496
+ - `no_reply`: For automated system emails
497
+ - `notification`: For user notifications
498
+ - `support`: For support emails
499
+ - `marketing`: For marketing emails
500
+
501
+ **Environment Variables**:
502
+ ```bash
503
+ AZURE_COMMUNICATION_ENDPOINT=https://xxx.communication.azure.com
504
+ AZURE_COMMUNICATION_ACCESS_KEY=your-access-key
505
+ AZURE_COMMUNICATION_EMAIL_SENDER_NOTIFICATION=DoNotReply@xxx.azurecomm.net
506
+ AZURE_COMMUNICATION_EMAIL_SENDER_NO_REPLY=DoNotReply@xxx.azurecomm.net
507
+ ```
508
+
509
+ ### **Cloudflare R2 Object Storage Pattern**
510
+
511
+ **Purpose**: Store and manage files in Cloudflare R2 (S3-compatible storage).
512
+
513
+ ```python
514
+ from core.services.object_storage import ObjectStorageService
515
+
516
+ # Initialize storage service
517
+ storage = ObjectStorageService()
518
+
519
+ # Upload file
520
+ public_url, storage_key = storage.upload_file(
521
+ uploaded_file=request.FILES['avatar'],
522
+ key=f"users/{user_id}/avatar"
523
+ )
524
+
525
+ # Upload user avatar
526
+ public_url, key = storage.upload_user_avatar(user_id, uploaded_file)
527
+
528
+ # Upload workspace logo
529
+ public_url, key = storage.upload_workspace_logo(workspace_id, uploaded_file)
530
+
531
+ # Upload workspace media
532
+ public_url, key = storage.upload_workspace_media(workspace_id, uploaded_file)
533
+
534
+ # Delete file
535
+ storage.delete_by_key(storage_key)
536
+
537
+ # Upload from URL (e.g., social media image)
538
+ public_url, key = storage.upload_image_from_url(
539
+ image_url="https://example.com/image.jpg",
540
+ key=f"users/{user_id}/avatar"
541
+ )
542
+
543
+ # Extract storage key from URL
544
+ storage_key = storage.key_from_url(public_url)
545
+ ```
546
+
547
+ **File Validation**:
548
+ ```python
549
+ from core.services.object_storage import (
550
+ validate_uploaded_file,
551
+ IMAGE_MIME_TYPES,
552
+ AVATAR_MIME_TYPES
553
+ )
554
+
555
+ # Validate file
556
+ error = validate_uploaded_file(
557
+ uploaded_file,
558
+ allowed_mime_types=IMAGE_MIME_TYPES,
559
+ max_size_bytes=5 * 1024 * 1024 # 5MB
560
+ )
561
+ if error:
562
+ return error_response(message=error, code=400)
563
+ ```
564
+
565
+ **Environment Variables**:
566
+ ```bash
567
+ R2_ACCOUNT_ID=your-account-id
568
+ R2_ACCESS_KEY_ID=your-access-key-id
569
+ R2_SECRET_ACCESS_KEY=your-secret-access-key
570
+ R2_BUCKET=your-bucket-name
571
+ R2_PUBLIC_BASE_URL=https://pub-xxx.r2.dev
572
+ R2_MEDIA_PATH=media
573
+ ```
574
+
575
+ ### **Sentry Error Tracking Pattern**
576
+
577
+ **Purpose**: Automatic error tracking and performance monitoring.
578
+
579
+ **Configuration**: Sentry is automatically initialized when `SENTRY_DSN` is set.
580
+
581
+ ```python
582
+ # Environment variable
583
+ SENTRY_DSN=https://xxx@sentry.io/xxx
584
+
585
+ # Sentry automatically captures:
586
+ # - Uncaught exceptions
587
+ # - All log levels (if enable_logs=True)
588
+ # - Performance traces (traces_sample_rate=1.0)
589
+ # - Profile data (profiles_sample_rate=1.0)
590
+ # - Request headers and IP (send_default_pii=True)
591
+ ```
592
+
593
+ **Manual Error Reporting**:
594
+ ```python
595
+ import sentry_sdk
596
+
597
+ # Capture exception
598
+ try:
599
+ risky_operation()
600
+ except Exception as e:
601
+ sentry_sdk.capture_exception(e)
602
+ raise
603
+
604
+ # Capture message
605
+ sentry_sdk.capture_message("Something important happened")
606
+
607
+ # Add context
608
+ with sentry_sdk.configure_scope() as scope:
609
+ scope.set_user({"id": user.id, "email": user.email})
610
+ scope.set_tag("environment", "production")
611
+ ```
612
+
613
+ ## 🐳 Docker and Production Patterns
614
+
615
+ ### **Docker Multi-Stage Build Pattern**
616
+
617
+ **Purpose**: Optimized production Docker builds with minimal image size.
618
+
619
+ ```dockerfile
620
+ # Multi-stage build (already configured in Dockerfile)
621
+ FROM python:3.12-slim as builder
622
+ # Install build dependencies and compile packages
623
+
624
+ FROM python:3.12-slim as runtime
625
+ # Copy only compiled packages and application code
626
+ # Result: Smaller production image
627
+ ```
628
+
629
+ ### **Nginx Reverse Proxy Pattern**
630
+
631
+ **Purpose**: Serve static files and proxy application requests.
632
+
633
+ ```nginx
634
+ # config/http.conf (already configured)
635
+ server {
636
+ listen 80;
637
+
638
+ # WebSocket support
639
+ location /ws/ {
640
+ proxy_pass http://127.0.0.1:8000;
641
+ proxy_http_version 1.1;
642
+ proxy_set_header Upgrade $http_upgrade;
643
+ proxy_set_header Connection "upgrade";
644
+ }
645
+
646
+ # Regular HTTP requests
647
+ location / {
648
+ proxy_pass http://127.0.0.1:8000;
649
+ proxy_set_header Host $host;
650
+ proxy_set_header X-Real-IP $remote_addr;
651
+ }
652
+ }
653
+ ```
654
+
655
+ ### **Gunicorn + Uvicorn Workers Pattern**
656
+
657
+ **Purpose**: Production-grade ASGI server with multiple workers.
658
+
659
+ ```python
660
+ # config/gunicorn_config.py (already configured)
661
+ command = '{project_name}.asgi:application'
662
+ pythonpath = '.'
663
+ bind = '0.0.0.0:8000'
664
+ workers = 4 # Auto-calculated based on CPU count
665
+ worker_class = 'uvicorn.workers.UvicornWorker'
666
+ ```
667
+
668
+ ### **Entry Script Pattern**
669
+
670
+ **Purpose**: Graceful service startup and shutdown.
671
+
672
+ ```bash
673
+ # .entry/start_core_backend.sh (already configured)
674
+ # Start nginx in background
675
+ nginx -c /app/config/http.conf
676
+
677
+ # Start gunicorn in background
678
+ gunicorn config.gunicorn_config &
679
+
680
+ # Handle graceful shutdown on SIGTERM/SIGINT
681
+ trap 'kill -TERM $NGINX_PID; kill -TERM $GUNICORN_PID' TERM INT
682
+
683
+ # Wait for processes
684
+ wait $GUNICORN_PID
685
+ ```
686
+
687
+ ## 🔒 Production Configuration Pattern
688
+
689
+ ### **Environment-Based Settings Pattern**
690
+
691
+ **Purpose**: Separate configurations for development and production.
692
+
693
+ ```python
694
+ # settings/base.py (shared settings)
695
+ DEBUG = env.bool('DEBUG', default=False)
696
+ SENTRY_DSN = env('SENTRY_DSN', default='')
697
+ AZURE_COMMUNICATION_ENDPOINT = env('AZURE_COMMUNICATION_ENDPOINT', default='')
698
+
699
+ # settings/dev.py (development overrides)
700
+ DEBUG = True
701
+
702
+ # settings/prod.py (production overrides)
703
+ DEBUG = False
704
+ SESSION_COOKIE_SECURE = True
705
+ CSRF_COOKIE_SECURE = True
706
+ SECURE_SSL_REDIRECT = True
707
+ SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
708
+ ```
709
+
710
+ ### **ASGI/WSGI Production Settings Pattern**
711
+
712
+ **Purpose**: Use production settings in Docker environment.
713
+
714
+ ```python
715
+ # asgi.py (uses prod settings)
716
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings.prod')
717
+
718
+ # wsgi.py (uses prod settings)
719
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', '{project_name}.settings.prod')
720
+ ```
721
+
722
+ This design pattern documentation ensures consistent, maintainable, and scalable code across the entire project. AI assistants should follow these patterns when working with the codebase.