A cross-platform .NET global tool that bootstraps opinionated Clean Architecture solutions with Domain-Driven Design in seconds.
- Introduction
- Solution Structure
- Features
- Requirements
- Installation
- Quick Start
- Command Reference
- Contributing
- Roadmap
- License
- Contact
DotNetArch removes the friction of setting up a well-structured .NET application. The tool generates a fully wired solution with clearly separated layers, ready-made infrastructure, and sensible defaults so you can focus on your domain from day one.
MyApp/
├── MyApp.Core/
├── MyApp.Application/
├── MyApp.Infrastructure/
├── MyApp.API/
│ └── Config/
│ ├── Env/
│ │ ├── .env.development
│ │ ├── .env.test
│ │ └── .env.production
│ └── Settings/
│ ├── appsettings.development.json
│ ├── appsettings.test.json
│ └── appsettings.production.json
├── Dockerfile
├── docker-compose.yml
├── dotnet-arch.yml
└── README.md
This layout exposes all the moving parts up front—application layers, environment configs, and Docker assets.
- One-command solution scaffolding with Core, Application, Infrastructure, and API layers—choose controller or fast minimal APIs.
- Git & docs bootstrap: initializes a repository, adds a
.gitignore, and drops in a README template. - Docker-friendly: generates Dockerfile and
docker-compose.yml;exec --dockerbuilds images, starts containers, streams logs, and cleans everything up safely on exit. - Detached Docker lifecycle:
exec --docker-detachruns containers in the background with step-by-step logging, andexec --docker-stopsafely stops and removes them when you're done. - Environment-specific configuration: prepopulated
.envandappsettingsfiles for development, test, and production underAPI/Config. - Vertical-slice CRUD generation using MediatR, FluentValidation, and Unit-of-Work based EF Core repositories with pagination helpers.
- Unit-of-Work repositories are created automatically so your data layer is production-ready from the start.
- Incremental CRUD generation: re‑runs complement missing parts (handlers, controllers/endpoints, repositories, UoW, DbContext) instead of failing or duplicating.
- Custom action scaffolding for additional commands or queries without breaking existing slices.
- Standard vs. non‑standard actions: exact, case‑insensitive match to CRUD keywords (Create, Update, Delete, GetById, GetAll, GetList, Patch) triggers full CRUD behavior; any other name is treated as non‑standard.
- No‑DB friendly: even without a database provider, a minimal Core Entity (Id only) is generated so Application code compiles. Handlers are skeletons and repositories contain TODO bodies.
- Controller purity: controllers use Application models only (no Entity usings). GET methods return IActionResult with NotFound/Ok.
- Robust controller updates: existing method detection uses regex (not substring) to avoid false positives (e.g., Update vs UpdateTelegram).
- Event scaffolding to create domain events and interactively wire subscribers across features.
- Enum scaffolding to generate strongly typed enumerations per feature or globally under
Core/Common/Enums. - Service scaffolder for custom services, Redis caches, RabbitMQ message brokers, or outbound HTTP clients with resilient
HttpRequestwrappers and automatic DI registration. - Database provider selection (SQL Server, SQLite, PostgreSQL, MongoDB, or No Database) stored for reuse across commands.
- Idempotent updates: running commands again augments existing files instead of duplicating them.
- Automatic NuGet package and service registration, including Swagger and startup configuration.
- Exec command auto-migrates: detects property changes, creates missing migrations, applies them, and then runs the API or Docker container.
- Cross-platform and tested on Windows, macOS, and Linux.
- Target framework auto-detect (net8/net9): picks the highest installed SDK (8+), sets the solution TFM, and generates a
global.jsonwithrollForward: latestMajorfor smooth upgrades. - Per-TFM package alignment: EF Core, OpenAPI, Microsoft.Extensions.* and related packages match the selected TFM (8.x or 9.x) to prevent version mismatches.
- .NET SDK 8.0+ (the CLI targets
net8.0andnet9.0automatically) - Supported OS: Windows 10+, macOS Catalina+, or any modern Linux distribution
- Git for cloning or contributing
Check your .NET version:
dotnet --versionInstall or update the tool globally from NuGet:
dotnet tool install --global DotNetArch
# or
dotnet tool update --global DotNetArchGenerate a new solution, scaffold an entity, and run the API:
dotnet-arch new solution MyApp
cd MyApp
# optional: choose API style, startup project, and database provider interactively
# env and appsettings for dev/test/prod are under ./config
dotnet-arch new crud --entity Product
# creates controllers/endpoints, unit-of-work repository, handlers, validators, and migrations
dotnet-arch new event --entity Product --event Created
# scaffolds ProductCreatedEvent and lets you add subscribers
dotnet-arch new enum --entity Product --enum Status
# creates Status enum under Core/Features/Products/Enums
dotnet-arch new enum --enum Priority
# creates Priority enum under Core/Common/Enums
dotnet-arch new constant --entity Product --constant Fields
# creates Fields class under Core/Features/Products/Constants
dotnet-arch new constant --constant AppSettings
# creates AppSettings class under Core/Common/Constants
dotnet-arch new service
# choose "HttpRequest" to generate a typed HTTP client wrapper
dotnet-arch exec --docker
# builds an image, starts a container, auto-applies migrations, and cleans it up on exit
dotnet-arch exec --docker-detach
# builds an image, starts a container in the background, logs setup steps, and leaves it running
dotnet-arch exec --docker-stop
# safely stops and removes the detached container and imageMissing options are prompted with sane defaults, keeping the experience smooth for newcomers.
DotNetArch is multi-targeted so you can keep several SDKs installed without friction.
- The tool ships for both
net8.0andnet9.0. When running it from the repository, use the helper scripts to pick the best match automatically:- macOS/Linux:
./scripts/run.sh -- --help - Windows (PowerShell):
pwsh ./scripts/run.ps1 -- --help - Prefer manual control? Run
dotnet run -f net8.0 -- --help(ornet9.0) instead.
- macOS/Linux:
- Newly generated solutions include a
global.jsonwithrollForwardset tolatestMajor, so your projects transparently adopt the highest installed .NET 8/9 SDK. - During scaffolding DotNetArch inspects
dotnet --list-sdksand selects the highest supported target framework, ensuring the produced projects match your environment.
dotnet-arch new solution <SolutionName> [--output=Path] [--startup=ProjectName] [--style=controller|fast] [--no-database]Creates a clean, feature-based solution. Initializes Git, writes a README template, scaffolds .env and appsettings files for development, test, and production, optionally adds Docker assets, and records choices in dotnet-arch.yml for later commands.
Notes
- If
--no-databaseis supplied, the scaffold skips EF Core setup and migrations. A minimal Core Entity (Id only) is still generated so the Application layer compiles. - The target framework is detected from installed SDKs (8+). A
global.jsonwithrollForward: latestMajoris added, and package versions are aligned to the selected TFM.
dotnet-arch new crud --entity=EntityName [--output=Path]Generates a full vertical slice for an entity with CQRS handlers, validators, Unit‑of‑Work repositories, API endpoints, and migrations.
Notes
- Re‑runs are incremental: missing parts are added; existing code is left intact.
- In no‑DB mode a minimal Entity with
Idis still created so Application code compiles; repository methods are generated with TODO bodies. - Standard CRUD endpoints/actions created:
- Commands: Create, Update, Delete
- Queries: GetById, GetAll, GetList
- Update command includes
Id; controllers sendcommand with { Id = id }.
dotnet-arch new action --entity=EntityName [--action=ActionName] --method=METHOD [--output=Path]Adds a custom command or query to an existing slice. After choosing the HTTP verb, you're prompted for an optional action name—leaving it blank infers a CRUD-style name from the method. The scaffolder infers command versus query based on the HTTP verb. If the slice is missing, a minimal repository and controller are created.
Behavior
- Exact, case‑insensitive CRUD names (Create, Update, Delete, GetById, GetAll, GetList, Patch) are treated as “standard” and generate full end‑to‑end code (handlers, validators, controller/endpoints, and repository methods).
- Any other name is “non‑standard”: no database logic is generated. With a DB provider configured, you’ll be asked whether to add a matching repository method; signatures are parameterless.
- Non‑standard commands and queries do not take inputs:
- Controller:
public async Task<IActionResult> MyAction()sendsnew MyActionCommand()/new MyActionQuery() - Minimal API:
routes.MapX("/Api/<Entity>/MyAction", async (IMediator m) => …)
- Controller:
- Standard Update always includes
Idin the command. Controllers usecommand with { Id = id }.
Validation
- The method/action conflict check only applies to exact keywords (case‑insensitive). Examples:
- POST + Create → allowed; POST + Update → error; POST + UpdateTelegram → allowed.
dotnet-arch new event --entity=EntityName [--event=EventName] [--output=Path]Creates a domain event for an entity and interactively adds subscribers from other slices.
dotnet-arch new enum [--entity=EntityName] --enum=EnumName [--output=Path]Creates an enum for an existing entity under Core/Features/<Entity>/Enums. If no entity is supplied, the enum is placed in Core/Common/Enums.
dotnet-arch new constant [--entity=EntityName] --constant=ConstantName [--output=Path]Creates a constants class for an existing entity under Core/Features/<Entity>/Constants. If no entity is supplied, the class is placed in Core/Common/Constants.
dotnet-arch new service [--output=Path]Interactive scaffolding for:
- Custom services (per‑feature or common)
- Cache services powered by Redis
- Message broker services using RabbitMQ
- HttpRequest service wrapper for calling external APIs
Interfaces and implementations are placed in the appropriate layer and registered automatically.
dotnet-arch exec [--output=Path] [--docker] [--docker-detach] [--docker-stop]Launches the startup project. Detects entity property changes, creates and applies migrations automatically (skipped in No Database mode), and then runs the API. With --docker, builds the image, starts the container, streams logs, and tears everything down safely on exit. With --docker-detach, performs the same setup with step-by-step logging but leaves the container running in the background without streaming logs or cleaning up. With --docker-stop, safely stops and removes the container and image from a detached run.
dotnet-arch remove migration [--output=Path]Rolls the database back one migration and deletes the last migration file without running the application.
Contributions are welcome! To get started:
- Fork the repository and create a branch:
git checkout -b feature/awesome-thing - Build to ensure everything compiles:
dotnet build - Commit your changes following conventional commits if possible
- Push and open a pull request
Please open an issue for large features to discuss your proposal first.
- Support for additional architectural styles (microservices, event-driven)
- Extended template customization options
- Deeper integrations with libraries like MassTransit
- Optional GUI for non-CLI users
- GitHub Issues: Report a problem
- Email: [email protected]
- LinkedIn: Moein Rezaee
Start simplifying your .NET project setup today with DotNetArch! 🚀
- Controllers never reference Core Entities; they use Application Models. Legacy
using <Solution>.Core.Features.<Entity>.Entities;directives are removed on update. - GET endpoints return IActionResult with NotFound/Ok to prevent null-cast issues.
- Regex‑based method detection prevents collisions like
UpdateversusUpdateTelegramwhen augmenting controllers. - Non‑standard actions (commands/queries) are parameterless by default for both controllers and minimal APIs.
- In no‑DB runs, a minimal Entity class (Id only) is generated to satisfy type references; repository methods contain TODO placeholders.
- Re‑running scaffolds augments files in place; it won’t overwrite your custom logic.