vega-framework 0.1.0__tar.gz

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.

Potentially problematic release.


This version of vega-framework might be problematic. Click here for more details.

Files changed (30) hide show
  1. vega_framework-0.1.0/LICENSE +21 -0
  2. vega_framework-0.1.0/PKG-INFO +116 -0
  3. vega_framework-0.1.0/README.md +89 -0
  4. vega_framework-0.1.0/pyproject.toml +77 -0
  5. vega_framework-0.1.0/vega/__init__.py +43 -0
  6. vega_framework-0.1.0/vega/cli/__init__.py +5 -0
  7. vega_framework-0.1.0/vega/cli/commands/__init__.py +1 -0
  8. vega_framework-0.1.0/vega/cli/commands/generate.py +337 -0
  9. vega_framework-0.1.0/vega/cli/commands/init.py +280 -0
  10. vega_framework-0.1.0/vega/cli/main.py +109 -0
  11. vega_framework-0.1.0/vega/cli/scaffolds/__init__.py +7 -0
  12. vega_framework-0.1.0/vega/cli/scaffolds/fastapi.py +108 -0
  13. vega_framework-0.1.0/vega/cli/templates/__init__.py +35 -0
  14. vega_framework-0.1.0/vega/cli/templates/components.py +322 -0
  15. vega_framework-0.1.0/vega/cli/utils/__init__.py +11 -0
  16. vega_framework-0.1.0/vega/cli/utils/messages.py +77 -0
  17. vega_framework-0.1.0/vega/cli/utils/naming.py +114 -0
  18. vega_framework-0.1.0/vega/cli/utils/validators.py +113 -0
  19. vega_framework-0.1.0/vega/di/__init__.py +52 -0
  20. vega_framework-0.1.0/vega/di/container.py +175 -0
  21. vega_framework-0.1.0/vega/di/decorators.py +276 -0
  22. vega_framework-0.1.0/vega/di/errors.py +6 -0
  23. vega_framework-0.1.0/vega/di/scope.py +148 -0
  24. vega_framework-0.1.0/vega/patterns/__init__.py +40 -0
  25. vega_framework-0.1.0/vega/patterns/interactor.py +76 -0
  26. vega_framework-0.1.0/vega/patterns/mediator.py +75 -0
  27. vega_framework-0.1.0/vega/patterns/repository.py +47 -0
  28. vega_framework-0.1.0/vega/patterns/service.py +42 -0
  29. vega_framework-0.1.0/vega/settings/__init__.py +34 -0
  30. vega_framework-0.1.0/vega/settings/base.py +46 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Vega Framework Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,116 @@
1
+ Metadata-Version: 2.3
2
+ Name: vega-framework
3
+ Version: 0.1.0
4
+ Summary: Lightweight Python framework for Clean Architecture with automatic dependency injection
5
+ License: MIT
6
+ Keywords: clean-architecture,dependency-injection,framework,python,async,vega
7
+ Author: Roberto Ferro
8
+ Requires-Python: >=3.10,<4.0
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Dist: click (>=8.0,<9.0)
20
+ Requires-Dist: pydantic (>=2.0,<3.0)
21
+ Requires-Dist: pydantic-settings (>=2.0,<3.0)
22
+ Project-URL: Documentation, https://vega-framework.readthedocs.io
23
+ Project-URL: Homepage, https://github.com/your-org/vega-framework
24
+ Project-URL: Repository, https://github.com/your-org/vega-framework
25
+ Description-Content-Type: text/markdown
26
+
27
+ # Vega Framework
28
+
29
+ A lightweight Python framework for building applications with **Clean Architecture** principles.
30
+
31
+ ## Features
32
+
33
+ - ✅ **Automatic Dependency Injection** - Zero boilerplate, type-safe DI
34
+ - ✅ **Clean Architecture Patterns** - Interactor, Mediator, Repository, Service
35
+ - ✅ **Scope Management** - Singleton, Scoped, Transient lifetimes
36
+ - ✅ **Type-Safe** - Full type hints support
37
+ - ✅ **Framework-Agnostic** - Works with any domain (web, AI, IoT, fintech, etc.)
38
+ - ✅ **CLI Scaffolding** - Generate projects and components instantly
39
+ - ✅ **Lightweight** - No unnecessary dependencies
40
+
41
+ ## Installation
42
+
43
+ ```bash
44
+ pip install vega-framework
45
+ ```
46
+
47
+ ## Quick Start
48
+
49
+ ```bash
50
+ # Create new project
51
+ vega init my-app
52
+
53
+ # Generate components
54
+ vega generate entity User
55
+ vega generate repository UserRepository
56
+ vega generate interactor CreateUser
57
+
58
+ # Create FastAPI project
59
+ vega init my-api --template fastapi
60
+ ```
61
+
62
+ ## CLI Commands
63
+
64
+ ### Initialize Project
65
+
66
+ ```bash
67
+ vega init <project_name> [--template basic|fastapi|ai-rag] [--path .]
68
+ ```
69
+
70
+ Creates a new Vega project with Clean Architecture structure:
71
+
72
+ - `domain/` - Entities, repositories, services, interactors
73
+ - `application/` - Mediators and workflows
74
+ - `infrastructure/` - Repository and service implementations
75
+ - `config.py` - DI container setup
76
+ - `settings.py` - Application configuration
77
+
78
+ ### Generate Components
79
+
80
+ ```bash
81
+ vega generate entity <Name>
82
+ vega generate repository <Name> [--impl memory|sql]
83
+ vega generate service <Name>
84
+ vega generate interactor <Name>
85
+ vega generate mediator <Name>
86
+ vega generate web fastapi
87
+ ```
88
+
89
+ ### Validate Project
90
+
91
+ ```bash
92
+ vega doctor [--path .]
93
+ ```
94
+
95
+ Validates project structure, DI configuration, and architecture compliance.
96
+
97
+ ## Use Cases
98
+
99
+ Perfect for:
100
+
101
+ - AI/RAG applications
102
+ - E-commerce platforms
103
+ - Fintech systems
104
+ - Mobile backends
105
+ - Microservices
106
+ - CLI tools
107
+ - Any Python application requiring clean architecture
108
+
109
+ ## License
110
+
111
+ MIT
112
+
113
+ ## Contributing
114
+
115
+ Contributions welcome! This framework is extracted from production code and battle-tested.
116
+
@@ -0,0 +1,89 @@
1
+ # Vega Framework
2
+
3
+ A lightweight Python framework for building applications with **Clean Architecture** principles.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Automatic Dependency Injection** - Zero boilerplate, type-safe DI
8
+ - ✅ **Clean Architecture Patterns** - Interactor, Mediator, Repository, Service
9
+ - ✅ **Scope Management** - Singleton, Scoped, Transient lifetimes
10
+ - ✅ **Type-Safe** - Full type hints support
11
+ - ✅ **Framework-Agnostic** - Works with any domain (web, AI, IoT, fintech, etc.)
12
+ - ✅ **CLI Scaffolding** - Generate projects and components instantly
13
+ - ✅ **Lightweight** - No unnecessary dependencies
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install vega-framework
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```bash
24
+ # Create new project
25
+ vega init my-app
26
+
27
+ # Generate components
28
+ vega generate entity User
29
+ vega generate repository UserRepository
30
+ vega generate interactor CreateUser
31
+
32
+ # Create FastAPI project
33
+ vega init my-api --template fastapi
34
+ ```
35
+
36
+ ## CLI Commands
37
+
38
+ ### Initialize Project
39
+
40
+ ```bash
41
+ vega init <project_name> [--template basic|fastapi|ai-rag] [--path .]
42
+ ```
43
+
44
+ Creates a new Vega project with Clean Architecture structure:
45
+
46
+ - `domain/` - Entities, repositories, services, interactors
47
+ - `application/` - Mediators and workflows
48
+ - `infrastructure/` - Repository and service implementations
49
+ - `config.py` - DI container setup
50
+ - `settings.py` - Application configuration
51
+
52
+ ### Generate Components
53
+
54
+ ```bash
55
+ vega generate entity <Name>
56
+ vega generate repository <Name> [--impl memory|sql]
57
+ vega generate service <Name>
58
+ vega generate interactor <Name>
59
+ vega generate mediator <Name>
60
+ vega generate web fastapi
61
+ ```
62
+
63
+ ### Validate Project
64
+
65
+ ```bash
66
+ vega doctor [--path .]
67
+ ```
68
+
69
+ Validates project structure, DI configuration, and architecture compliance.
70
+
71
+ ## Use Cases
72
+
73
+ Perfect for:
74
+
75
+ - AI/RAG applications
76
+ - E-commerce platforms
77
+ - Fintech systems
78
+ - Mobile backends
79
+ - Microservices
80
+ - CLI tools
81
+ - Any Python application requiring clean architecture
82
+
83
+ ## License
84
+
85
+ MIT
86
+
87
+ ## Contributing
88
+
89
+ Contributions welcome! This framework is extracted from production code and battle-tested.
@@ -0,0 +1,77 @@
1
+ [tool.poetry]
2
+ name = "vega-framework"
3
+ version = "0.1.0"
4
+ description = "Lightweight Python framework for Clean Architecture with automatic dependency injection"
5
+ authors = ["Roberto Ferro"]
6
+ license = "MIT"
7
+ readme = "README.md"
8
+ homepage = "https://github.com/your-org/vega-framework"
9
+ repository = "https://github.com/your-org/vega-framework"
10
+ documentation = "https://vega-framework.readthedocs.io"
11
+ keywords = [
12
+ "clean-architecture",
13
+ "dependency-injection",
14
+ "framework",
15
+ "python",
16
+ "async",
17
+ "vega",
18
+ ]
19
+ classifiers = [
20
+ "Development Status :: 3 - Alpha",
21
+ "Intended Audience :: Developers",
22
+ "License :: OSI Approved :: MIT License",
23
+ "Programming Language :: Python :: 3",
24
+ "Programming Language :: Python :: 3.10",
25
+ "Programming Language :: Python :: 3.11",
26
+ "Programming Language :: Python :: 3.12",
27
+ "Topic :: Software Development :: Libraries :: Application Frameworks",
28
+ "Topic :: Software Development :: Libraries :: Python Modules",
29
+ ]
30
+ packages = [{ include = "vega", from = "." }]
31
+
32
+ [tool.poetry.dependencies]
33
+ python = "^3.10"
34
+ pydantic = "^2.0"
35
+ pydantic-settings = "^2.0"
36
+ click = "^8.0"
37
+
38
+ [tool.poetry.group.dev.dependencies]
39
+ pytest = "^7.0"
40
+ pytest-asyncio = "^0.21"
41
+ pytest-cov = "^4.0"
42
+ black = "^23.0"
43
+ isort = "^5.0"
44
+ mypy = "^1.0"
45
+ ruff = "^0.1"
46
+
47
+ [tool.poetry.scripts]
48
+ vega = "vega.cli.main:cli"
49
+
50
+ [build-system]
51
+ requires = ["poetry-core>=1.0.0"]
52
+ build-backend = "poetry.core.masonry.api"
53
+
54
+ [tool.black]
55
+ line-length = 100
56
+ target-version = ['py310']
57
+
58
+ [tool.isort]
59
+ profile = "black"
60
+ line_length = 100
61
+
62
+ [tool.mypy]
63
+ python_version = "3.10"
64
+ warn_return_any = true
65
+ warn_unused_configs = true
66
+ disallow_untyped_defs = true
67
+
68
+ [tool.pytest.ini_options]
69
+ asyncio_mode = "auto"
70
+ testpaths = ["tests"]
71
+ python_files = ["test_*.py"]
72
+ python_classes = ["Test*"]
73
+ python_functions = ["test_*"]
74
+
75
+ [tool.ruff]
76
+ line-length = 100
77
+ target-version = "py310"
@@ -0,0 +1,43 @@
1
+ """
2
+ CleanArch Framework
3
+
4
+ A lightweight Python framework for building applications with Clean Architecture.
5
+
6
+ Features:
7
+ - Automatic Dependency Injection
8
+ - Clean Architecture patterns (Interactor, Mediator, Repository)
9
+ - Type-safe with Python type hints
10
+ - Scoped dependency management (Singleton, Scoped, Transient)
11
+ - CLI scaffolding tools
12
+
13
+ Example:
14
+ from vega.patterns import Interactor
15
+ from vega.di import bind
16
+
17
+ class CreateUser(Interactor[User]):
18
+ def __init__(self, name: str, email: str):
19
+ self.name = name
20
+ self.email = email
21
+
22
+ @bind
23
+ async def call(self, repository: UserRepository) -> User:
24
+ user = User(name=self.name, email=self.email)
25
+ return await repository.save(user)
26
+ """
27
+
28
+ __version__ = "0.1.0"
29
+ __author__ = "CleanArch Contributors"
30
+
31
+ from vega.di import bind, injectable, Scope, scope_context
32
+ from vega.patterns import Interactor, Mediator, Repository, Service
33
+
34
+ __all__ = [
35
+ "bind",
36
+ "injectable",
37
+ "Scope",
38
+ "scope_context",
39
+ "Interactor",
40
+ "Mediator",
41
+ "Repository",
42
+ "Service",
43
+ ]
@@ -0,0 +1,5 @@
1
+ """Vega Framework CLI tools"""
2
+
3
+ from vega.cli.main import cli
4
+
5
+ __all__ = ["cli"]
@@ -0,0 +1 @@
1
+ """CLI commands"""
@@ -0,0 +1,337 @@
1
+ """Generate command - Create components in Vega project"""
2
+ import click
3
+ import re
4
+ from pathlib import Path
5
+
6
+ from vega.cli.templates import (
7
+ render_entity,
8
+ render_infrastructure_repository,
9
+ render_infrastructure_service,
10
+ render_interactor,
11
+ render_mediator,
12
+ render_repository_interface,
13
+ render_service_interface,
14
+ )
15
+ from vega.cli.scaffolds import create_fastapi_scaffold
16
+
17
+
18
+
19
+ def to_snake_case(name: str) -> str:
20
+ """Convert CamelCase to snake_case"""
21
+ name = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
22
+ return re.sub('([a-z0-9])([A-Z])', r'\1_\2', name).lower()
23
+
24
+
25
+ def to_pascal_case(name: str) -> str:
26
+ """Convert strings to PascalCase, handling separators and camelCase input"""
27
+ cleaned = name.strip()
28
+ if not cleaned:
29
+ return ""
30
+
31
+ # Normalize common separators to spaces
32
+ normalized = cleaned.replace('-', ' ').replace('_', ' ')
33
+ if ' ' in normalized:
34
+ parts = normalized.split()
35
+ else:
36
+ parts = re.findall(r'[A-Z]+(?=$|[A-Z][a-z0-9])|[A-Z]?[a-z0-9]+|[0-9]+', cleaned)
37
+ if not parts:
38
+ parts = [cleaned]
39
+
40
+ def _pascal_piece(piece: str) -> str:
41
+ return piece if piece.isupper() else piece[:1].upper() + piece[1:].lower()
42
+
43
+ return ''.join(_pascal_piece(part) for part in parts if part)
44
+
45
+
46
+ def _resolve_implementation_names(class_name: str, implementation: str) -> tuple[str, str]:
47
+ """Derive implementation class and file names from flag input."""
48
+ impl_pascal = to_pascal_case(implementation) or "Impl"
49
+ base = class_name
50
+
51
+ if impl_pascal.lower() in {"impl", "implementation"}:
52
+ impl_class = f"{base}{impl_pascal}"
53
+ elif base.lower().startswith(impl_pascal.lower()):
54
+ impl_class = base
55
+ else:
56
+ impl_class = f"{impl_pascal}{base}"
57
+
58
+ impl_file = to_snake_case(impl_class)
59
+ return impl_class, impl_file
60
+
61
+
62
+ def generate_component(
63
+ component_type: str,
64
+ name: str,
65
+ project_path: str,
66
+ implementation: str | None = None,
67
+ ):
68
+ """Generate a component in the Vega project"""
69
+
70
+ project_root = Path(project_path).resolve()
71
+
72
+ # Check if we're in a Vega project
73
+ if not (project_root / "config.py").exists():
74
+ click.echo(click.style("ERROR: Error: Not a Vega project (config.py not found)", fg='red'))
75
+ click.echo(" Run this command from your project root, or use --path option")
76
+ return
77
+
78
+ # Get project name from directory
79
+ project_name = project_root.name
80
+
81
+ class_name = to_pascal_case(name)
82
+ implementation = implementation.strip() if implementation else None
83
+ if component_type == "web":
84
+ _generate_fastapi_web(project_root, project_name, name)
85
+ return
86
+
87
+
88
+ suffixes = {
89
+ "repository": "Repository",
90
+ "service": "Service",
91
+ "mediator": "Mediator",
92
+ }
93
+
94
+ if implementation and component_type not in {'repository', 'service'}:
95
+ click.echo(
96
+ click.style(
97
+ "WARNING: Implementation option is only supported for repositories and services",
98
+ fg='yellow',
99
+ )
100
+ )
101
+ implementation = None
102
+
103
+ if component_type in suffixes:
104
+ suffix = suffixes[component_type]
105
+ if class_name.lower().endswith(suffix.lower()):
106
+ class_name = f"{class_name[:-len(suffix)]}{suffix}"
107
+ else:
108
+ class_name = f"{class_name}{suffix}"
109
+
110
+ file_name = to_snake_case(class_name)
111
+
112
+ if component_type == 'entity':
113
+ _generate_entity(project_root, project_name, class_name, file_name)
114
+ elif component_type == 'repository':
115
+ _generate_repository(project_root, project_name, class_name, file_name, implementation)
116
+ elif component_type == 'service':
117
+ _generate_service(project_root, project_name, class_name, file_name, implementation)
118
+ elif component_type == 'interactor':
119
+ _generate_interactor(project_root, project_name, class_name, file_name)
120
+ elif component_type == 'mediator':
121
+ _generate_mediator(project_root, project_name, class_name, file_name)
122
+
123
+
124
+ def _generate_entity(project_root: Path, project_name: str, class_name: str, file_name: str):
125
+ """Generate domain entity"""
126
+
127
+ file_path = project_root / "domain" / "entities" / f"{file_name}.py"
128
+
129
+ if file_path.exists():
130
+ click.echo(click.style(f"ERROR: Error: {file_path} already exists", fg='red'))
131
+ return
132
+
133
+ content = render_entity(class_name)
134
+
135
+ file_path.write_text(content)
136
+ click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
137
+
138
+
139
+ def _generate_repository(
140
+ project_root: Path,
141
+ project_name: str,
142
+ class_name: str,
143
+ file_name: str,
144
+ implementation: str | None = None,
145
+ ):
146
+ """Generate repository interface"""
147
+
148
+ # Remove 'Repository' suffix if present to get entity name
149
+ entity_name = class_name[:-len('Repository')] if class_name.endswith('Repository') else class_name
150
+ entity_file = to_snake_case(entity_name)
151
+
152
+ file_path = project_root / "domain" / "repositories" / f"{file_name}.py"
153
+
154
+ if file_path.exists():
155
+ click.echo(click.style(f"ERROR: Error: {file_path} already exists", fg='red'))
156
+ return
157
+
158
+ # Check if entity exists
159
+ entity_path = project_root / "domain" / "entities" / f"{entity_file}.py"
160
+ if not entity_path.exists():
161
+ click.echo(
162
+ click.style(
163
+ f"⚠️ Warning: Entity {entity_name} does not exist at {entity_path.relative_to(project_root)}",
164
+ fg='yellow',
165
+ )
166
+ )
167
+
168
+ if click.confirm(f"Do you want to create the entity {entity_name}?", default=True):
169
+ _generate_entity(project_root, project_name, entity_name, entity_file)
170
+ click.echo() # Empty line for readability
171
+ else:
172
+ click.echo(click.style(f"ERROR: Cannot create repository without entity {entity_name}", fg='red'))
173
+ return
174
+
175
+ content = render_repository_interface(class_name, entity_name, entity_file)
176
+
177
+ file_path.write_text(content)
178
+ click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
179
+
180
+ # Suggest next steps
181
+ click.echo(f"\n💡 Next steps:")
182
+ click.echo(f" 1. Create entity: vega generate entity {entity_name}")
183
+ click.echo(f" 2. Implement repository in infrastructure/repositories/")
184
+ click.echo(f" 3. Register in config.py SERVICES dict")
185
+
186
+ if implementation:
187
+ _generate_infrastructure_repository(
188
+ project_root,
189
+ class_name,
190
+ file_name,
191
+ entity_name,
192
+ entity_file,
193
+ implementation,
194
+ )
195
+
196
+
197
+ def _generate_service(
198
+ project_root: Path,
199
+ project_name: str,
200
+ class_name: str,
201
+ file_name: str,
202
+ implementation: str | None = None,
203
+ ):
204
+ """Generate service interface"""
205
+
206
+ file_path = project_root / "domain" / "services" / f"{file_name}.py"
207
+
208
+ if file_path.exists():
209
+ click.echo(click.style(f"ERROR: Error: {file_path} already exists", fg='red'))
210
+ return
211
+
212
+ content = render_service_interface(class_name)
213
+
214
+ file_path.write_text(content)
215
+ click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
216
+
217
+ click.echo(f"\n💡 Next steps:")
218
+ click.echo(f" 1. Implement service in infrastructure/services/")
219
+ click.echo(f" 2. Register in config.py SERVICES dict")
220
+
221
+ if implementation:
222
+ _generate_infrastructure_service(
223
+ project_root,
224
+ class_name,
225
+ file_name,
226
+ implementation,
227
+ )
228
+
229
+
230
+ def _generate_interactor(project_root: Path, project_name: str, class_name: str, file_name: str):
231
+ """Generate interactor (use case)"""
232
+
233
+ # Try to infer entity from name (e.g., CreateUser -> User)
234
+ entity_name = class_name
235
+ for prefix in ['Create', 'Update', 'Delete', 'Get', 'List', 'Find']:
236
+ if class_name.startswith(prefix):
237
+ entity_name = class_name[len(prefix):]
238
+ break
239
+
240
+ entity_file = to_snake_case(entity_name)
241
+
242
+ file_path = project_root / "domain" / "interactors" / f"{file_name}.py"
243
+
244
+ if file_path.exists():
245
+ click.echo(click.style(f"ERROR: Error: {file_path} already exists", fg='red'))
246
+ return
247
+
248
+ content = render_interactor(class_name, entity_name, entity_file)
249
+
250
+ file_path.write_text(content)
251
+ click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
252
+
253
+ click.echo(f"\n💡 Usage:")
254
+ click.echo(f" result = await {class_name}(param=value)")
255
+
256
+
257
+ def _generate_mediator(project_root: Path, project_name: str, class_name: str, file_name: str):
258
+ """Generate mediator (workflow)"""
259
+
260
+ file_path = project_root / "application" / "mediators" / f"{file_name}.py"
261
+
262
+ if file_path.exists():
263
+ click.echo(click.style(f"ERROR: Error: {file_path} already exists", fg='red'))
264
+ return
265
+
266
+ content = render_mediator(class_name)
267
+
268
+ file_path.write_text(content)
269
+ click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
270
+
271
+ click.echo(f"\n💡 Usage:")
272
+ click.echo(f" result = await {class_name}(param=value)")
273
+
274
+
275
+ def _generate_infrastructure_repository(
276
+ project_root: Path,
277
+ interface_class_name: str,
278
+ interface_file_name: str,
279
+ entity_name: str,
280
+ entity_file: str,
281
+ implementation: str,
282
+ ) -> None:
283
+ """Generate infrastructure repository implementation extending the domain interface."""
284
+ impl_class, impl_file = _resolve_implementation_names(interface_class_name, implementation)
285
+ file_path = project_root / "infrastructure" / "repositories" / f"{impl_file}.py"
286
+
287
+ if file_path.exists():
288
+ click.echo(click.style(f"WARNING: Implementation {file_path} already exists", fg='yellow'))
289
+ return
290
+
291
+ file_path.parent.mkdir(parents=True, exist_ok=True)
292
+
293
+ content = render_infrastructure_repository(
294
+ impl_class,
295
+ interface_class_name,
296
+ interface_file_name,
297
+ entity_name,
298
+ entity_file,
299
+ )
300
+
301
+ file_path.write_text(content)
302
+ click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
303
+
304
+
305
+ def _generate_infrastructure_service(
306
+ project_root: Path,
307
+ interface_class_name: str,
308
+ interface_file_name: str,
309
+ implementation: str,
310
+ ) -> None:
311
+ """Generate infrastructure service implementation extending the domain interface."""
312
+ impl_class, impl_file = _resolve_implementation_names(interface_class_name, implementation)
313
+ file_path = project_root / "infrastructure" / "services" / f"{impl_file}.py"
314
+
315
+ if file_path.exists():
316
+ click.echo(click.style(f"WARNING: Implementation {file_path} already exists", fg='yellow'))
317
+ return
318
+
319
+ file_path.parent.mkdir(parents=True, exist_ok=True)
320
+
321
+ content = render_infrastructure_service(
322
+ impl_class,
323
+ interface_class_name,
324
+ interface_file_name,
325
+ )
326
+
327
+ file_path.write_text(content)
328
+ click.echo(f"+ Created {click.style(str(file_path.relative_to(project_root)), fg='green')}")
329
+
330
+ def _generate_fastapi_web(project_root: Path, project_name: str, name: str) -> None:
331
+ """Generate FastAPI web scaffold"""
332
+ if name.lower() not in {"fastapi", "fast-api"}:
333
+ click.echo(click.style("ERROR: Unsupported web scaffold. Use: vega generate web fastapi", fg='red'))
334
+ return
335
+
336
+ create_fastapi_scaffold(project_root, project_name)
337
+