vega-framework 0.1.15__py3-none-any.whl → 0.1.17__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.
- vega/cli/__init__.py +7 -1
- vega/cli/commands/generate.py +138 -0
- vega/cli/commands/migrate.py +3 -2
- vega/cli/main.py +5 -2
- vega/cli/templates/__init__.py +4 -0
- vega/cli/templates/cli/command.py.j2 +40 -0
- vega/cli/templates/cli/command_simple.py.j2 +19 -0
- vega/cli/templates/components.py +48 -0
- vega/cli/templates/project/ARCHITECTURE.md.j2 +267 -1
- vega/cli/templates/project/README.md.j2 +133 -14
- vega/cli/templates/project/config.py.j2 +2 -0
- vega/cli/templates/project/main_fastapi.py.j2 +28 -0
- vega/cli/templates/project/main_standard.py.j2 +25 -1
- vega/cli/utils/__init__.py +3 -0
- vega/cli/utils/async_support.py +55 -0
- vega_framework-0.1.17.dist-info/METADATA +483 -0
- {vega_framework-0.1.15.dist-info → vega_framework-0.1.17.dist-info}/RECORD +20 -17
- vega_framework-0.1.15.dist-info/METADATA +0 -158
- {vega_framework-0.1.15.dist-info → vega_framework-0.1.17.dist-info}/WHEEL +0 -0
- {vega_framework-0.1.15.dist-info → vega_framework-0.1.17.dist-info}/entry_points.txt +0 -0
- {vega_framework-0.1.15.dist-info → vega_framework-0.1.17.dist-info}/licenses/LICENSE +0 -0
vega/cli/__init__.py
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
"""Vega Framework CLI tools"""
|
2
2
|
|
3
|
-
|
3
|
+
# Lazy import to avoid circular dependencies when importing utilities
|
4
|
+
def __getattr__(name: str):
|
5
|
+
if name == "cli":
|
6
|
+
from vega.cli.main import cli
|
7
|
+
return cli
|
8
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
9
|
+
|
4
10
|
|
5
11
|
__all__ = ["cli"]
|
vega/cli/commands/generate.py
CHANGED
@@ -14,6 +14,8 @@ from vega.cli.templates import (
|
|
14
14
|
render_fastapi_router,
|
15
15
|
render_fastapi_middleware,
|
16
16
|
render_sqlalchemy_model,
|
17
|
+
render_cli_command,
|
18
|
+
render_cli_command_simple,
|
17
19
|
)
|
18
20
|
from vega.cli.scaffolds import create_fastapi_scaffold
|
19
21
|
|
@@ -124,6 +126,8 @@ def generate_component(
|
|
124
126
|
_generate_middleware(project_root, project_name, class_name, file_name)
|
125
127
|
elif component_type == 'model':
|
126
128
|
_generate_sqlalchemy_model(project_root, project_name, class_name, file_name)
|
129
|
+
elif component_type == 'command':
|
130
|
+
_generate_command(project_root, project_name, name, implementation)
|
127
131
|
|
128
132
|
|
129
133
|
def _generate_entity(project_root: Path, project_name: str, class_name: str, file_name: str):
|
@@ -670,3 +674,137 @@ from infrastructure.models.{file_name} import {class_name}Model # noqa: F401
|
|
670
674
|
click.echo(click.style(f'''
|
671
675
|
from infrastructure.models.{file_name} import {class_name}Model # noqa: F401
|
672
676
|
''', fg='cyan'))
|
677
|
+
|
678
|
+
|
679
|
+
def _generate_command(project_root: Path, project_name: str, name: str, is_async: str | None = None) -> None:
|
680
|
+
"""Generate a CLI command"""
|
681
|
+
|
682
|
+
# Check if presentation/cli exists
|
683
|
+
cli_path = project_root / "presentation" / "cli"
|
684
|
+
if not cli_path.exists():
|
685
|
+
cli_path.mkdir(parents=True, exist_ok=True)
|
686
|
+
click.echo(f"+ Created {click.style(str(cli_path.relative_to(project_root)), fg='green')}")
|
687
|
+
|
688
|
+
# Create commands directory if it doesn't exist
|
689
|
+
commands_path = cli_path / "commands"
|
690
|
+
commands_path.mkdir(exist_ok=True)
|
691
|
+
|
692
|
+
# Check if __init__.py exists
|
693
|
+
init_file = commands_path / "__init__.py"
|
694
|
+
if not init_file.exists():
|
695
|
+
init_file.write_text('"""CLI Commands"""\n')
|
696
|
+
click.echo(f"+ Created {click.style(str(init_file.relative_to(project_root)), fg='green')}")
|
697
|
+
|
698
|
+
# Convert name to snake_case for command and file
|
699
|
+
command_name = to_snake_case(name).replace('_', '-')
|
700
|
+
file_name = to_snake_case(name)
|
701
|
+
|
702
|
+
# Generate command file
|
703
|
+
command_file = commands_path / f"{file_name}.py"
|
704
|
+
|
705
|
+
if command_file.exists():
|
706
|
+
click.echo(click.style(f"ERROR: Error: {command_file.relative_to(project_root)} already exists", fg='red'))
|
707
|
+
return
|
708
|
+
|
709
|
+
# Determine if async (default is async unless explicitly set to 'sync' or 'simple')
|
710
|
+
use_async = is_async not in ['sync', 'simple', 'false', 'no'] if is_async else True
|
711
|
+
|
712
|
+
# Prompt for command details
|
713
|
+
description = click.prompt("Command description", default=f"{name} command")
|
714
|
+
|
715
|
+
# Ask if user wants to add options/arguments
|
716
|
+
add_params = click.confirm("Add options or arguments?", default=False)
|
717
|
+
|
718
|
+
options = []
|
719
|
+
arguments = []
|
720
|
+
params_list = []
|
721
|
+
|
722
|
+
if add_params:
|
723
|
+
click.echo("\nAdd options (e.g., --name, --email). Press Enter when done.")
|
724
|
+
while True:
|
725
|
+
opt_name = click.prompt("Option name (without --)", default="", show_default=False)
|
726
|
+
if not opt_name:
|
727
|
+
break
|
728
|
+
opt_type = click.prompt("Type", default="str", type=click.Choice(['str', 'int', 'bool']))
|
729
|
+
opt_required = click.confirm("Required?", default=False)
|
730
|
+
opt_help = click.prompt("Help text", default=f"{opt_name.replace('-', ' ').replace('_', ' ')}")
|
731
|
+
|
732
|
+
params_list.append(opt_name.replace('-', '_'))
|
733
|
+
|
734
|
+
opt_params = f"help='{opt_help}'"
|
735
|
+
if opt_required:
|
736
|
+
opt_params += ", required=True"
|
737
|
+
if opt_type != 'str':
|
738
|
+
if opt_type == 'bool':
|
739
|
+
opt_params += ", is_flag=True"
|
740
|
+
else:
|
741
|
+
opt_params += f", type={opt_type}"
|
742
|
+
|
743
|
+
options.append({
|
744
|
+
"flag": f"--{opt_name}",
|
745
|
+
"params": opt_params
|
746
|
+
})
|
747
|
+
|
748
|
+
click.echo("\nAdd arguments (positional). Press Enter when done.")
|
749
|
+
while True:
|
750
|
+
arg_name = click.prompt("Argument name", default="", show_default=False)
|
751
|
+
if not arg_name:
|
752
|
+
break
|
753
|
+
arg_required = click.confirm("Required?", default=True)
|
754
|
+
|
755
|
+
params_list.append(arg_name)
|
756
|
+
|
757
|
+
arg_params = "" if arg_required else ", required=False"
|
758
|
+
arguments.append({
|
759
|
+
"name": arg_name,
|
760
|
+
"params": arg_params
|
761
|
+
})
|
762
|
+
|
763
|
+
params_signature = ", ".join(params_list) if params_list else ""
|
764
|
+
|
765
|
+
# Ask about interactor usage
|
766
|
+
with_interactor = False
|
767
|
+
interactor_name = ""
|
768
|
+
if use_async:
|
769
|
+
with_interactor = click.confirm("Will this command use an interactor?", default=True)
|
770
|
+
if with_interactor:
|
771
|
+
interactor_name = click.prompt("Interactor name", default=f"{to_pascal_case(name)}")
|
772
|
+
|
773
|
+
usage_example = f"python main.py {command_name}"
|
774
|
+
if params_list:
|
775
|
+
usage_example += " " + " ".join([f"--{p.replace('_', '-')}=value" if f"--{p.replace('_', '-')}" in str(options) else p for p in params_list])
|
776
|
+
|
777
|
+
# Generate content
|
778
|
+
if use_async:
|
779
|
+
content = render_cli_command(
|
780
|
+
command_name=file_name,
|
781
|
+
description=description,
|
782
|
+
options=options,
|
783
|
+
arguments=arguments,
|
784
|
+
params_signature=params_signature,
|
785
|
+
params_list=params_list,
|
786
|
+
with_interactor=with_interactor,
|
787
|
+
usage_example=usage_example,
|
788
|
+
interactor_name=interactor_name,
|
789
|
+
)
|
790
|
+
else:
|
791
|
+
content = render_cli_command_simple(
|
792
|
+
command_name=file_name,
|
793
|
+
description=description,
|
794
|
+
options=options,
|
795
|
+
arguments=arguments,
|
796
|
+
params_signature=params_signature,
|
797
|
+
params_list=params_list,
|
798
|
+
)
|
799
|
+
|
800
|
+
command_file.write_text(content)
|
801
|
+
click.echo(f"+ Created {click.style(str(command_file.relative_to(project_root)), fg='green')}")
|
802
|
+
|
803
|
+
# Instructions for next steps
|
804
|
+
click.echo(f"\nNext steps:")
|
805
|
+
click.echo(f" 1. Implement your command logic in {command_file.relative_to(project_root)}")
|
806
|
+
click.echo(f" 2. Import and register in main.py:")
|
807
|
+
click.echo(click.style(f" from presentation.cli.commands.{file_name} import {file_name}", fg='cyan'))
|
808
|
+
click.echo(click.style(f" cli.add_command({file_name})", fg='cyan'))
|
809
|
+
if with_interactor:
|
810
|
+
click.echo(f" 3. Create interactor: vega generate interactor {interactor_name}")
|
vega/cli/commands/migrate.py
CHANGED
@@ -107,9 +107,9 @@ def history():
|
|
107
107
|
@migrate.command()
|
108
108
|
def init():
|
109
109
|
"""Initialize database with current schema (create tables)"""
|
110
|
-
import asyncio
|
111
110
|
from pathlib import Path
|
112
111
|
import sys
|
112
|
+
from vega.cli.utils import async_command
|
113
113
|
|
114
114
|
# Add project root to path to allow imports
|
115
115
|
project_root = Path.cwd()
|
@@ -123,13 +123,14 @@ def init():
|
|
123
123
|
click.echo("Make sure you have SQLAlchemy configured in your project")
|
124
124
|
sys.exit(1)
|
125
125
|
|
126
|
+
@async_command
|
126
127
|
async def _init():
|
127
128
|
click.echo("Creating database tables...")
|
128
129
|
await db_manager.create_tables()
|
129
130
|
click.secho("Database tables created successfully", fg='green')
|
130
131
|
|
131
132
|
try:
|
132
|
-
|
133
|
+
_init()
|
133
134
|
except Exception as e:
|
134
135
|
click.secho(f"Failed to initialize database: {e}", fg='red')
|
135
136
|
sys.exit(1)
|
vega/cli/main.py
CHANGED
@@ -56,11 +56,11 @@ def init(project_name, template, path):
|
|
56
56
|
|
57
57
|
@cli.command()
|
58
58
|
@click.argument('component_type', type=click.Choice([
|
59
|
-
'entity', 'repository', 'repo', 'service', 'interactor', 'mediator', 'router', 'middleware', 'model'
|
59
|
+
'entity', 'repository', 'repo', 'service', 'interactor', 'mediator', 'router', 'middleware', 'model', 'command'
|
60
60
|
]))
|
61
61
|
@click.argument('name')
|
62
62
|
@click.option('--path', default='.', help='Project root path')
|
63
|
-
@click.option('--impl', default=None, help='Generate infrastructure implementation for repository/service (e.g., memory, sql)')
|
63
|
+
@click.option('--impl', default=None, help='Generate infrastructure implementation for repository/service (e.g., memory, sql) or command type (async, sync)')
|
64
64
|
def generate(component_type, name, path, impl):
|
65
65
|
"""
|
66
66
|
Generate a component in your Vega project.
|
@@ -75,6 +75,7 @@ def generate(component_type, name, path, impl):
|
|
75
75
|
router - FastAPI router (requires web module)
|
76
76
|
middleware - FastAPI middleware (requires web module)
|
77
77
|
model - SQLAlchemy model (requires sqlalchemy module)
|
78
|
+
command - CLI command (async by default)
|
78
79
|
|
79
80
|
Examples:
|
80
81
|
vega generate entity Product
|
@@ -85,6 +86,8 @@ def generate(component_type, name, path, impl):
|
|
85
86
|
vega generate router Product
|
86
87
|
vega generate middleware Logging
|
87
88
|
vega generate model User
|
89
|
+
vega generate command CreateUser
|
90
|
+
vega generate command ListUsers --impl sync
|
88
91
|
"""
|
89
92
|
# Normalize 'repo' to 'repository'
|
90
93
|
if component_type == 'repo':
|
vega/cli/templates/__init__.py
CHANGED
@@ -24,6 +24,8 @@ from .components import (
|
|
24
24
|
render_alembic_env,
|
25
25
|
render_alembic_script_mako,
|
26
26
|
render_sqlalchemy_model,
|
27
|
+
render_cli_command,
|
28
|
+
render_cli_command_simple,
|
27
29
|
)
|
28
30
|
from .loader import render_template
|
29
31
|
|
@@ -53,5 +55,7 @@ __all__ = [
|
|
53
55
|
"render_alembic_env",
|
54
56
|
"render_alembic_script_mako",
|
55
57
|
"render_sqlalchemy_model",
|
58
|
+
"render_cli_command",
|
59
|
+
"render_cli_command_simple",
|
56
60
|
"render_template",
|
57
61
|
]
|
@@ -0,0 +1,40 @@
|
|
1
|
+
"""{{ description }}"""
|
2
|
+
import click
|
3
|
+
from vega.cli.utils import async_command
|
4
|
+
import config # noqa: F401 - Initialize DI container
|
5
|
+
|
6
|
+
{% if with_interactor -%}
|
7
|
+
# Import your interactors here
|
8
|
+
# from domain.interactors.{{ interactor_name }} import {{ interactor_name }}
|
9
|
+
{% endif %}
|
10
|
+
|
11
|
+
@click.command()
|
12
|
+
{%- for option in options %}
|
13
|
+
@click.option('{{ option.flag }}', {{ option.params }})
|
14
|
+
{%- endfor %}
|
15
|
+
{%- for argument in arguments %}
|
16
|
+
@click.argument('{{ argument.name }}'{{ argument.params }})
|
17
|
+
{%- endfor %}
|
18
|
+
@async_command
|
19
|
+
async def {{ command_name }}({{ params_signature }}):
|
20
|
+
"""
|
21
|
+
{{ description }}
|
22
|
+
|
23
|
+
{% if usage_example -%}
|
24
|
+
Usage:
|
25
|
+
{{ usage_example }}
|
26
|
+
{% endif -%}
|
27
|
+
"""
|
28
|
+
{% if with_interactor -%}
|
29
|
+
# Example: Execute an interactor
|
30
|
+
# result = await MyInteractor(param=value)
|
31
|
+
# click.echo(f"Result: {result}")
|
32
|
+
{% endif -%}
|
33
|
+
|
34
|
+
# Your command implementation here
|
35
|
+
click.echo("{{ command_name }} executed!")
|
36
|
+
{% if params_signature -%}
|
37
|
+
{% for param in params_list -%}
|
38
|
+
click.echo(f" {{ param }}: {{'{'}}{{ param }}{{'}'}}")
|
39
|
+
{% endfor -%}
|
40
|
+
{% endif %}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
"""{{ description }}"""
|
2
|
+
import click
|
3
|
+
|
4
|
+
|
5
|
+
@click.command()
|
6
|
+
{%- for option in options %}
|
7
|
+
@click.option('{{ option.flag }}', {{ option.params }})
|
8
|
+
{%- endfor %}
|
9
|
+
{%- for argument in arguments %}
|
10
|
+
@click.argument('{{ argument.name }}'{{ argument.params }})
|
11
|
+
{%- endfor %}
|
12
|
+
def {{ command_name }}({{ params_signature }}):
|
13
|
+
"""{{ description }}"""
|
14
|
+
click.echo("{{ command_name }} executed!")
|
15
|
+
{% if params_signature -%}
|
16
|
+
{% for param in params_list -%}
|
17
|
+
click.echo(f" {{ param }}: {{'{'}}{{ param }}{{'}'}}")
|
18
|
+
{% endfor -%}
|
19
|
+
{% endif %}
|
vega/cli/templates/components.py
CHANGED
@@ -189,3 +189,51 @@ def render_sqlalchemy_model(class_name: str, table_name: str) -> str:
|
|
189
189
|
class_name=class_name,
|
190
190
|
table_name=table_name,
|
191
191
|
)
|
192
|
+
|
193
|
+
|
194
|
+
def render_cli_command(
|
195
|
+
command_name: str,
|
196
|
+
description: str,
|
197
|
+
options: list[dict],
|
198
|
+
arguments: list[dict],
|
199
|
+
params_signature: str,
|
200
|
+
params_list: list[str],
|
201
|
+
with_interactor: bool = True,
|
202
|
+
usage_example: str = "",
|
203
|
+
interactor_name: str = "",
|
204
|
+
) -> str:
|
205
|
+
"""Return the template for a CLI command"""
|
206
|
+
return render_template(
|
207
|
+
"command.py.j2",
|
208
|
+
subfolder="cli",
|
209
|
+
command_name=command_name,
|
210
|
+
description=description,
|
211
|
+
options=options,
|
212
|
+
arguments=arguments,
|
213
|
+
params_signature=params_signature,
|
214
|
+
params_list=params_list,
|
215
|
+
with_interactor=with_interactor,
|
216
|
+
usage_example=usage_example,
|
217
|
+
interactor_name=interactor_name,
|
218
|
+
)
|
219
|
+
|
220
|
+
|
221
|
+
def render_cli_command_simple(
|
222
|
+
command_name: str,
|
223
|
+
description: str,
|
224
|
+
options: list[dict],
|
225
|
+
arguments: list[dict],
|
226
|
+
params_signature: str,
|
227
|
+
params_list: list[str],
|
228
|
+
) -> str:
|
229
|
+
"""Return the template for a simple CLI command (non-async)"""
|
230
|
+
return render_template(
|
231
|
+
"command_simple.py.j2",
|
232
|
+
subfolder="cli",
|
233
|
+
command_name=command_name,
|
234
|
+
description=description,
|
235
|
+
options=options,
|
236
|
+
arguments=arguments,
|
237
|
+
params_signature=params_signature,
|
238
|
+
params_list=params_list,
|
239
|
+
)
|
@@ -262,6 +262,232 @@ class StripePaymentService(PaymentService):
|
|
262
262
|
pass
|
263
263
|
```
|
264
264
|
|
265
|
+
## Development Workflow with Vega CLI
|
266
|
+
|
267
|
+
Vega provides powerful CLI commands to quickly scaffold components following Clean Architecture principles. These commands help you maintain the correct architectural boundaries while accelerating development.
|
268
|
+
|
269
|
+
### Generating Domain Layer Components
|
270
|
+
|
271
|
+
**Entities** - Pure business objects:
|
272
|
+
```bash
|
273
|
+
vega generate entity User
|
274
|
+
vega generate entity Product
|
275
|
+
vega generate entity Order
|
276
|
+
```
|
277
|
+
|
278
|
+
**Repository Interfaces** - Data persistence abstractions:
|
279
|
+
```bash
|
280
|
+
vega generate repository UserRepository
|
281
|
+
vega generate repository Product # Auto-adds "Repository" suffix
|
282
|
+
```
|
283
|
+
|
284
|
+
**Repository with Implementation** - Create both interface and concrete implementation:
|
285
|
+
```bash
|
286
|
+
vega generate repository User --impl memory # In-memory implementation
|
287
|
+
vega generate repository User --impl sql # SQL implementation
|
288
|
+
vega generate repository User --impl postgres # PostgreSQL implementation
|
289
|
+
```
|
290
|
+
|
291
|
+
**Service Interfaces** - External service abstractions:
|
292
|
+
```bash
|
293
|
+
vega generate service EmailService
|
294
|
+
vega generate service PaymentService
|
295
|
+
```
|
296
|
+
|
297
|
+
**Service with Implementation**:
|
298
|
+
```bash
|
299
|
+
vega generate service Email --impl sendgrid
|
300
|
+
vega generate service Payment --impl stripe
|
301
|
+
```
|
302
|
+
|
303
|
+
**Interactors** - Single-purpose use cases:
|
304
|
+
```bash
|
305
|
+
vega generate interactor CreateUser
|
306
|
+
vega generate interactor GetUserById
|
307
|
+
vega generate interactor UpdateUserEmail
|
308
|
+
vega generate interactor DeleteUser
|
309
|
+
```
|
310
|
+
|
311
|
+
### Generating Application Layer Components
|
312
|
+
|
313
|
+
**Mediators** - Complex workflows:
|
314
|
+
```bash
|
315
|
+
vega generate mediator UserRegistrationFlow
|
316
|
+
vega generate mediator CheckoutWorkflow
|
317
|
+
vega generate mediator OrderProcessingPipeline
|
318
|
+
```
|
319
|
+
|
320
|
+
### Generating Infrastructure Layer Components
|
321
|
+
|
322
|
+
**SQLAlchemy Models** - Database models (requires database support):
|
323
|
+
```bash
|
324
|
+
vega generate model User
|
325
|
+
vega generate model Product
|
326
|
+
vega generate model Order
|
327
|
+
```
|
328
|
+
|
329
|
+
Note: Models are automatically registered in Alembic for migrations.
|
330
|
+
|
331
|
+
### Generating Presentation Layer Components
|
332
|
+
|
333
|
+
**CLI Commands** - Command-line interfaces:
|
334
|
+
```bash
|
335
|
+
vega generate command CreateUser # Async command (default)
|
336
|
+
vega generate command ListUsers --impl sync # Synchronous command
|
337
|
+
```
|
338
|
+
|
339
|
+
The generator will interactively prompt for:
|
340
|
+
- Command description
|
341
|
+
- Options and arguments (flags, parameters)
|
342
|
+
- Whether to use interactors
|
343
|
+
- Parameter types and validation
|
344
|
+
|
345
|
+
**FastAPI Routers** - HTTP API endpoints (requires web support):
|
346
|
+
```bash
|
347
|
+
vega generate router User
|
348
|
+
vega generate router Product
|
349
|
+
vega generate router Order
|
350
|
+
```
|
351
|
+
|
352
|
+
**FastAPI Middleware** - Request/response processing (requires web support):
|
353
|
+
```bash
|
354
|
+
vega generate middleware Logging
|
355
|
+
vega generate middleware Authentication
|
356
|
+
vega generate middleware RateLimiting
|
357
|
+
```
|
358
|
+
|
359
|
+
### Adding Features to Existing Projects
|
360
|
+
|
361
|
+
**Add FastAPI Web Support**:
|
362
|
+
```bash
|
363
|
+
vega add web
|
364
|
+
```
|
365
|
+
|
366
|
+
Creates complete FastAPI scaffold:
|
367
|
+
- `presentation/web/` directory structure
|
368
|
+
- Routes and middleware setup
|
369
|
+
- Health check endpoints
|
370
|
+
- App factory pattern
|
371
|
+
|
372
|
+
**Add Database Support**:
|
373
|
+
```bash
|
374
|
+
vega add sqlalchemy # or: vega add db
|
375
|
+
```
|
376
|
+
|
377
|
+
Adds complete database infrastructure:
|
378
|
+
- SQLAlchemy async support
|
379
|
+
- Alembic migrations
|
380
|
+
- Database manager
|
381
|
+
- Base model classes
|
382
|
+
|
383
|
+
### Database Migrations Workflow
|
384
|
+
|
385
|
+
After adding SQLAlchemy support, manage your database schema:
|
386
|
+
|
387
|
+
```bash
|
388
|
+
# Initialize database (creates tables)
|
389
|
+
vega migrate init
|
390
|
+
|
391
|
+
# Create migration after model changes
|
392
|
+
vega migrate create -m "Add users table"
|
393
|
+
vega migrate create -m "Add email_verified field to users"
|
394
|
+
|
395
|
+
# Apply pending migrations
|
396
|
+
vega migrate upgrade
|
397
|
+
|
398
|
+
# Rollback last migration
|
399
|
+
vega migrate downgrade
|
400
|
+
|
401
|
+
# Check current migration status
|
402
|
+
vega migrate current
|
403
|
+
|
404
|
+
# View migration history
|
405
|
+
vega migrate history
|
406
|
+
```
|
407
|
+
|
408
|
+
### Project Validation
|
409
|
+
|
410
|
+
Validate your project structure and architecture compliance:
|
411
|
+
|
412
|
+
```bash
|
413
|
+
vega doctor
|
414
|
+
vega doctor --path ./my-project
|
415
|
+
```
|
416
|
+
|
417
|
+
Checks for:
|
418
|
+
- Correct folder structure
|
419
|
+
- DI container configuration
|
420
|
+
- Import dependencies
|
421
|
+
- Architecture violations (e.g., domain depending on infrastructure)
|
422
|
+
|
423
|
+
### Development Best Practices with CLI
|
424
|
+
|
425
|
+
**1. Start with Domain Layer**:
|
426
|
+
```bash
|
427
|
+
# Define your entities first
|
428
|
+
vega generate entity User
|
429
|
+
|
430
|
+
# Create repository interfaces
|
431
|
+
vega generate repository UserRepository
|
432
|
+
|
433
|
+
# Implement use cases
|
434
|
+
vega generate interactor CreateUser
|
435
|
+
vega generate interactor GetUserById
|
436
|
+
```
|
437
|
+
|
438
|
+
**2. Add Infrastructure Implementations**:
|
439
|
+
```bash
|
440
|
+
# Generate repository implementation
|
441
|
+
vega generate repository User --impl memory # Start with in-memory
|
442
|
+
|
443
|
+
# Later, add database support
|
444
|
+
vega add sqlalchemy
|
445
|
+
vega generate model User
|
446
|
+
vega generate repository User --impl sql
|
447
|
+
```
|
448
|
+
|
449
|
+
**3. Build Application Workflows**:
|
450
|
+
```bash
|
451
|
+
# Orchestrate multiple use cases
|
452
|
+
vega generate mediator UserRegistrationFlow
|
453
|
+
```
|
454
|
+
|
455
|
+
**4. Create Delivery Mechanisms**:
|
456
|
+
```bash
|
457
|
+
# CLI interface
|
458
|
+
vega generate command create-user
|
459
|
+
|
460
|
+
# Web API (add web first if not present)
|
461
|
+
vega add web
|
462
|
+
vega generate router User
|
463
|
+
```
|
464
|
+
|
465
|
+
**5. Validate Architecture**:
|
466
|
+
```bash
|
467
|
+
# Ensure clean architecture compliance
|
468
|
+
vega doctor
|
469
|
+
```
|
470
|
+
|
471
|
+
### CLI Quick Reference
|
472
|
+
|
473
|
+
| Command | Purpose |
|
474
|
+
|---------|---------|
|
475
|
+
| `vega init <name>` | Create new Vega project |
|
476
|
+
| `vega generate entity <Name>` | Generate domain entity |
|
477
|
+
| `vega generate repository <Name>` | Generate repository interface |
|
478
|
+
| `vega generate interactor <Name>` | Generate use case |
|
479
|
+
| `vega generate mediator <Name>` | Generate workflow |
|
480
|
+
| `vega generate command <Name>` | Generate CLI command |
|
481
|
+
| `vega generate router <Name>` | Generate FastAPI router |
|
482
|
+
| `vega generate model <Name>` | Generate SQLAlchemy model |
|
483
|
+
| `vega add web` | Add FastAPI support |
|
484
|
+
| `vega add sqlalchemy` | Add database support |
|
485
|
+
| `vega migrate <command>` | Manage database migrations |
|
486
|
+
| `vega doctor` | Validate project architecture |
|
487
|
+
| `vega update` | Update Vega Framework |
|
488
|
+
|
489
|
+
For complete documentation, see the [README](README.md#cli-commands).
|
490
|
+
|
265
491
|
## Dependency Injection
|
266
492
|
|
267
493
|
Vega provides automatic dependency injection through decorators and a container.
|
@@ -507,7 +733,47 @@ class PriceCalculator:
|
|
507
733
|
pass
|
508
734
|
```
|
509
735
|
|
510
|
-
### 6.
|
736
|
+
### 6. Async CLI Commands
|
737
|
+
|
738
|
+
Vega provides the `@async_command` decorator to use async/await in Click CLI commands, allowing you to execute interactors seamlessly:
|
739
|
+
|
740
|
+
```python
|
741
|
+
import click
|
742
|
+
from vega.cli.utils import async_command
|
743
|
+
|
744
|
+
@click.command()
|
745
|
+
@click.option('--name', required=True)
|
746
|
+
@click.option('--email', required=True)
|
747
|
+
@async_command
|
748
|
+
async def create_user(name: str, email: str):
|
749
|
+
"""Create a new user via CLI"""
|
750
|
+
# Import config to initialize DI container
|
751
|
+
import config # noqa: F401
|
752
|
+
from domain.interactors.create_user import CreateUser
|
753
|
+
|
754
|
+
# Execute async interactor
|
755
|
+
user = await CreateUser(name=name, email=email)
|
756
|
+
click.echo(f"Created user: {user.id} - {user.name}")
|
757
|
+
```
|
758
|
+
|
759
|
+
**Benefits:**
|
760
|
+
- Execute async interactors directly in CLI commands
|
761
|
+
- Same business logic works in both CLI and Web (FastAPI) contexts
|
762
|
+
- Clean async/await syntax
|
763
|
+
- Automatic asyncio event loop management
|
764
|
+
|
765
|
+
**Alternative short syntax:**
|
766
|
+
```python
|
767
|
+
from vega.cli.utils import coro # Alias for async_command
|
768
|
+
|
769
|
+
@click.command()
|
770
|
+
@coro
|
771
|
+
async def my_command():
|
772
|
+
result = await MyInteractor()
|
773
|
+
click.echo(result)
|
774
|
+
```
|
775
|
+
|
776
|
+
### 7. Testing
|
511
777
|
|
512
778
|
Vega's architecture makes testing straightforward:
|
513
779
|
|