Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@
"command": "filepath=\"$TOOL_INPUT_FILE_PATH\"; if echo \"$filepath\" | grep -qE '(\\.env|\\.github/workflows/|settings\\.local\\.json)$'; then echo 'BLOCK: Protected file - confirm with user first' >&2; exit 2; fi"
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash \"$CLAUDE_PROJECT_DIR/scripts/check-changelog-before-pr.sh\"",
"if": "Bash(gh pr create:*)"
}
]
}
],
"PostToolUse": [
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v7
with:
fetch-depth: 0

Expand All @@ -86,7 +86,7 @@ jobs:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Cache SonarQube Cloud packages
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
Expand Down Expand Up @@ -237,7 +237,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v7
with:
fetch-depth: 0

Expand All @@ -247,7 +247,7 @@ jobs:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Cache NuGet packages
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props') }}
Expand Down Expand Up @@ -276,7 +276,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v7
with:
fetch-depth: 0

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/copilot-setup-steps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v7

- name: Setup .NET 10
uses: actions/setup-dotnet@v5
Expand All @@ -34,7 +34,7 @@ jobs:
bun-version: latest

- name: Cache NuGet packages
uses: actions/cache@v4
uses: actions/cache@v6
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj', '**/*.props') }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
dotnet-version: ${{ env.DOTNET_VERSION }}

- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v7
with:
fetch-depth: 0

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-libs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v7
with:
fetch-depth: 0 # Fetching all history for version calculation and release notes (prev vs now)

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-template.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:

steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v7
with:
fetch-depth: 0

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate-scripts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v7

- name: Install PowerShell Script Analyzer
shell: pwsh
Expand Down
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Changelog

All notable changes to this project are documented in this file, grouped by date.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

## [2026-07-15]

### Added

- **EventMarkdownGenerator**: `Topic` and `FullyQualifiedTopicName` are now exposed separately on `EventMetadata`
and `EventViewModel`. `Topic` is the plain topic / event hub name; `FullyQualifiedTopicName` is the composed
`{env}.{domain}.{visibility}.{topic}.{version}` convention string.
- **EventMarkdownGenerator**: `--partition-key-attribute` option to discover partition keys by attribute name
(or name prefix), mirroring `--event-attribute`. Partition key discovery and its `Order` are now resolved
generically via reflection instead of a hardcoded `PartitionKeyAttribute` type.
- **EventMarkdownGenerator**: event attributes may expose an optional `EventName` property to override the
documented event name (falls back to the CLR type name when absent).

### Changed

- **EventMarkdownGenerator**: `EventMetadata.TopicName` / `EventViewModel.TopicName` replaced by
`FullyQualifiedTopicName`. The default `event.liquid` template now renders both `Topic` and
`Fully Qualified Topic`.
- **EventMarkdownGenerator**: multi-line XML doc property descriptions are flattened to a single line
(whitespace collapsed) so they no longer break the generated markdown payload table.

### Fixed

- **EventMarkdownGenerator**: property descriptions whose XML doc summary wrapped onto multiple lines
previously spilled out of their table cell as an orphaned line.
91 changes: 63 additions & 28 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,22 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
**Momentum .NET** is a comprehensive template system that generates production-ready microservices solutions using .NET 10. The repository contains both the template engine (`mmt`) and supporting library ecosystem.

**Key Components**:

- **Template System** (`dotnet new mmt`): Generates complete microservices solutions
- **Momentum Libraries**: Reusable .NET libraries for service defaults, extensions, and patterns
- **Sample Application** (AppDomain): Complete microservices example demonstrating patterns

## Changelog & Pull Requests

**Every PR must update `CHANGELOG.md` before it is created.** Add a `## [YYYY-MM-DD]` section (today's
date; reuse it if a section for today already exists) using the [Keep a Changelog](https://keepachangelog.com/)
categories (`Added`, `Changed`, `Deprecated`, `Removed`, `Fixed`, `Security`). Do this as the last step
before opening the PR.

A `PreToolUse` hook (`scripts/check-changelog-before-pr.sh`, wired in `.claude/settings.json`) blocks
`gh pr create` when `CHANGELOG.md` has not been touched on the current branch. If it blocks you, add a
changelog entry and retry.

## Development Commands

### Template Development and Testing
Expand Down Expand Up @@ -91,13 +103,15 @@ cd docs && bun install && bun run dev
```

**Aspire Database Setup**:

- PostgreSQL with persistent storage
- Automatic Liquibase migrations on startup
- PgAdmin for database management
- Health checks validate schema and constraints
- Services wait for database and migrations before starting

**Database Connection**:

- Services automatically get connection strings from Aspire
- Health checks validate connectivity and schema integrity
- LinqToDB configured with snake_case naming conventions
Expand Down Expand Up @@ -139,6 +153,7 @@ dotnet test libs/Momentum/tests/Momentum.Extensions.SourceGenerators.Tests
The template (`mmt`) generates microservices solutions with:

**Generated Project Structure**:

```
YourService/
├── src/
Expand All @@ -157,6 +172,7 @@ YourService/
```

**Template Configuration**:

- Components: `--api`, `--backoffice`, `--orleans`, `--aspire`, `--docs`, `--bff`
- Infrastructure: `--db-config [default|none|npgsql|liquibase]`, `--kafka`
- Customization: `--org "Company"`, `--port 8100`, `--no-sample`
Expand Down Expand Up @@ -188,6 +204,7 @@ YourService/
### Technology Stack Integration

**Core Technologies**:

- **.NET 10** with **Aspire**: Orchestration and observability
- **Wolverine**: CQRS message handling with PostgreSQL persistence
- **Orleans**: Stateful actor processing (optional)
Expand All @@ -196,6 +213,7 @@ YourService/
- **OpenTelemetry**: Distributed tracing and metrics

**Port Allocation** (base port configurable, default 8100):

- Aspire Dashboard: 18110 (HTTPS), 18100 (HTTP)
- API: 8111 (HTTPS), 8101 (HTTP), 8102 (gRPC)
- BackOffice: 8113 (HTTPS), 8103 (HTTP)
Expand All @@ -219,19 +237,22 @@ YourService/
The `--local` flag enables template testing with locally built Momentum libraries:

**What it does**:

- Copies `local-mmt-version.txt` and `local-feed-path.txt` to the generated project
- The post-setup tool reads these files and:
- Hardcodes the local version into `Directory.Packages.props` (`MomentumVersion`)
- Creates a `nuget.config` with the local NuGet feed source
- Cleans up the text files
- Hardcodes the local version into `Directory.Packages.props` (`MomentumVersion`)
- Creates a `nuget.config` with the local NuGet feed source
- Cleans up the text files
- Automatically used by `Run-TemplateTests.ps1` for template testing

**How the lib build generates local packages**:

- `libs/Momentum/Directory.Build.targets` generates version `1000.0.0-pre.{timestamp}`
- Packs all libraries to `libs/Momentum/.local/nuget/`
- Writes `local-mmt-version.txt` and `local-feed-path.txt` to the repo root

**Usage**:

```bash
# Build and pack libraries first
dotnet build libs/Momentum/Momentum.slnx
Expand Down Expand Up @@ -330,6 +351,7 @@ await messageBus.PublishAsync(new CustomerCreated(customer.Id, customer.Name));
### MSBuild Properties

Global configuration in `Directory.Build.props`:

- **Target Framework**: .NET 10.0
- **Nullable Reference Types**: Enabled
- **Analyzers**: .NET analyzers + SonarAnalyzer enabled
Expand All @@ -351,23 +373,27 @@ Template uses sophisticated conditional compilation:
### Template vs Library Development

The repository serves dual purposes:

- **Template development**: Testing and building the `mmt` template
- **Library development**: Standalone Momentum libraries in `libs/Momentum/`

### Database Migrations

Uses Liquibase for version-controlled schema management:

- Setup scripts in `infra/YourService.Database/Liquibase/`
- Automatic migration via Docker Compose
- Separate service_bus and application schemas

**IMPORTANT - Database Organization Rules**:

- **Table Definitions**: Each table should be in its own file under `tables/` directory
- **Constraints & Indexes**: Should be defined WITHIN the same file as the table they apply to
- **Avoid Separate Constraint Files**: Do NOT create separate `constraints_and_indexes.sql` files
- **Logical Grouping**: Keep related constraints, indexes, triggers, and functions with their tables

**Correct Structure**:

```
infra/YourService.Database/Liquibase/main/
├── main.sql # Schema creation
Expand All @@ -384,6 +410,7 @@ infra/YourService.Database/Liquibase/main/
```

**Example Table File Structure**:

```sql
-- Table creation
CREATE TABLE main.invoices (...);
Expand All @@ -410,59 +437,66 @@ CREATE TRIGGER tr_invoice_version BEFORE UPDATE ON main.invoices ...;
**Common Problems and Solutions**:

1. **Health Checks Failing**:
```bash
# Check if database is running
curl http://localhost:8101/status

# Check Aspire dashboard for service status
open https://localhost:18110
```
```bash
# Check if database is running
curl http://localhost:8101/status

# Check Aspire dashboard for service status
open https://localhost:18110
```

2. **Migration Issues**:
```bash
# Check Liquibase logs in Aspire dashboard
# Verify changelog.xml includes all table files
# Ensure changesets have unique IDs
```

```bash
# Check Liquibase logs in Aspire dashboard
# Verify changelog.xml includes all table files
# Ensure changesets have unique IDs
```

3. **Missing Constraints/Indexes**:
```sql
-- Verify constraints exist
SELECT constraint_name, constraint_type
FROM information_schema.table_constraints
WHERE table_schema = 'main';

-- Verify indexes exist
SELECT schemaname, tablename, indexname
FROM pg_indexes
WHERE schemaname = 'main';
```

```sql
-- Verify constraints exist
SELECT constraint_name, constraint_type
FROM information_schema.table_constraints
WHERE table_schema = 'main';

-- Verify indexes exist
SELECT schemaname, tablename, indexname
FROM pg_indexes
WHERE schemaname = 'main';
```

4. **Connection String Issues**:
- Aspire automatically configures connection strings
- Check appsettings.json for overrides
- Verify service references in AppHost Program.cs
- Aspire automatically configures connection strings
- Check appsettings.json for overrides
- Verify service references in AppHost Program.cs

### Observability Integration

Complete observability stack configured by default:

- **Structured Logging**: Serilog with enrichment
- **Metrics**: OpenTelemetry with custom meters
- **Tracing**: Distributed tracing across services
- **Health Checks**: Built-in health endpoints

**Health Check Endpoints**:

- `/status` - Liveness probe (cached status, no auth)
- `/health/internal` - Readiness probe (localhost only, detailed in dev)
- `/health` - Public health check (requires auth, detailed)

**Database Health Checks**:

- Connectivity validation
- Schema verification (tables, constraints, indexes)
- Basic data access testing
- Constraint and foreign key validation

**Aspire Configuration**:

- Database resources with health checks
- Liquibase migration dependencies
- Service orchestration with proper wait conditions
Expand All @@ -471,6 +505,7 @@ Complete observability stack configured by default:
### Development vs Production Configuration

Template generates environment-specific configurations:

- **Development**: Uses Aspire dashboard, verbose logging
- **Production**: Optimized for container deployment
- **Docker Compose**: Full-stack local development environment
Expand Down
Loading
Loading