# Phase 4: .NET Solution Design Design the target .NET 10 solution structure and map every Go item to its .NET counterpart. This phase translates the Go codebase decomposition (from Phases 1-2) and library mappings (from Phase 3) into a concrete .NET implementation plan. ## Objective Every module, feature, and test in the porting database must have either a .NET mapping (project, namespace, class, method) or a justified N/A status. The result is a complete blueprint for the porting work in Phase 6. ## Prerequisites - Phases 1-3 complete: all Go items in the DB, all libraries mapped - Verify with: `dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db` - Read and follow the [.NET Coding Standards](../../standards/dotnet-standards.md) — all naming, testing, and logging decisions must comply ## Source and Target Locations - **Go source code** is located in the `golang/` folder (specifically `golang/nats-server/`) - **.NET ported version** is located in the `dotnet/` folder ## Milestone Tracking This phase corresponds to **Milestone 4** in [Gitea](https://gitea.dohertylan.com/dohertj2/natsnet/milestone/4). When starting this phase, verify the milestone is open. Assign relevant issues to this milestone as work progresses. ### Issue Updates Each completion criterion has a corresponding Gitea issue. Update issues as you work: - **Starting a criterion**: Add a comment noting work has begun - **Blocked**: Add a comment describing the blocker - **Complete**: Close the issue with a comment summarizing the result (e.g., counts, verification output) Close issues via CLI: ```bash curl -s -X PATCH "https://gitea.dohertylan.com/api/v1/repos/dohertj2/natsnet/issues/{N}" \ -H "Content-Type: application/json" \ -H "Authorization: token $GITEA_TOKEN" \ -d '{"state":"closed"}' ``` Or close via the Gitea web UI. ## Solution Structure Define the .NET solution layout following standard conventions: ``` dotnet/ ZB.MOM.NatsNet.sln src/ ZB.MOM.NatsNet.Server/ # Main server library (all core logic) Protocol/ # Wire protocol parsing, commands Subscriptions/ # SubList trie, subject matching JetStream/ # Stream management, consumers Cluster/ # Routes, gateways, leaf nodes Auth/ # Authentication, accounts, JWT ... ZB.MOM.NatsNet.Server.Host/ # Host/entry point (Program.cs, DI, config) tests/ ZB.MOM.NatsNet.Server.Tests/ # Unit tests for ZB.MOM.NatsNet.Server Protocol/ Subscriptions/ JetStream/ ... ZB.MOM.NatsNet.Server.IntegrationTests/ # Cross-module and end-to-end tests ``` The `ZB.MOM.NatsNet.Server` project holds all portable logic. `ZB.MOM.NatsNet.Server.Host` is the thin entry point that wires up dependency injection, configuration, and hosting. Tests mirror the source structure. ## Naming Conventions Follow these rules consistently when mapping Go items to .NET: | Aspect | Convention | Example | |--------|-----------|---------| | Classes | PascalCase | `NatsParser`, `SubList`, `JetStreamController` | | Methods | PascalCase | `TryParse`, `Match`, `ProcessMessage` | | Namespaces | `ZB.MOM.NatsNet.Server.[Module]` | `ZB.MOM.NatsNet.Server.Protocol`, `ZB.MOM.NatsNet.Server.Subscriptions` | | Test classes | `[ClassName]Tests` | `NatsParserTests`, `SubListTests` | | Test methods | `[Method]_[Scenario]_[Expected]` | `TryParse_ValidInput_ReturnsTrue` | | Interfaces | `I[Name]` | `IMessageRouter`, `ISubListAccess` | | Projects | `ZB.MOM.NatsNet.Server[.Suffix]` | `ZB.MOM.NatsNet.Server`, `ZB.MOM.NatsNet.Server.Host` | Avoid abbreviations unless they are universally understood (e.g., `TCP`, `TLS`, `JWT`). Prefer descriptive names over short ones. ## Steps ### Step 1: Map modules For each module in the database, assign a .NET project, namespace, and class. The `--namespace` and `--class` options are optional but recommended. ```bash # List all modules to review dotnet run --project tools/NatsNet.PortTracker -- module list --db porting.db # Map a module to its .NET target dotnet run --project tools/NatsNet.PortTracker -- module map \ --project "ZB.MOM.NatsNet.Server" \ --namespace "ZB.MOM.NatsNet.Server.Protocol" \ --class "NatsParser" \ --db porting.db ``` Work through all modules systematically. Group related Go files into the same namespace: | Go package/file pattern | .NET namespace | |------------------------|----------------| | `server/parser.go` | `ZB.MOM.NatsNet.Server.Protocol` | | `server/sublist.go` | `ZB.MOM.NatsNet.Server.Subscriptions` | | `server/jetstream*.go` | `ZB.MOM.NatsNet.Server.JetStream` | | `server/route.go`, `server/gateway.go` | `ZB.MOM.NatsNet.Server.Cluster` | | `server/auth.go`, `server/accounts.go` | `ZB.MOM.NatsNet.Server.Auth` | | `server/pse/` | Likely N/A (Go-specific platform code) | ### Step 2: Map features For each feature (function/method), assign the .NET class and method name: ```bash # List features for a specific module dotnet run --project tools/NatsNet.PortTracker -- feature list --module --db porting.db # Map a feature dotnet run --project tools/NatsNet.PortTracker -- feature map \ --project "ZB.MOM.NatsNet.Server" \ --class "NatsParser" \ --method "TryParse" \ --db porting.db ``` When mapping Go functions to .NET methods: - Go free functions become static methods or instance methods on the appropriate class - Go methods with receivers map to instance methods on the corresponding .NET class - Go `init()` functions typically map to static constructors or initialization in DI setup - Go `goroutine` launches map to `Task`-based async methods ### Step 3: Map tests For each test function, assign the .NET test class and method: ```bash # List tests for a module dotnet run --project tools/NatsNet.PortTracker -- test list --module --db porting.db # Map a test dotnet run --project tools/NatsNet.PortTracker -- test map \ --project "ZB.MOM.NatsNet.Server.Tests" \ --class "NatsParserTests" \ --method "TryParse_ValidInput_ReturnsTrue" \ --db porting.db ``` Go test naming (`TestParserValid`) translates to .NET naming (`TryParse_ValidInput_ReturnsTrue`). Each Go `Test*` function maps to one or more `[Fact]` or `[Theory]` methods. Table-driven Go tests often become `[Theory]` with `[InlineData]` or `[MemberData]`. Use Shouldly for assertions and NSubstitute for mocking — see the [.NET Coding Standards](../../standards/dotnet-standards.md) for details. ### Step 4: Mark N/A items Some Go code has no .NET equivalent. Mark these with a clear reason: ```bash # Mark a module as N/A dotnet run --project tools/NatsNet.PortTracker -- module set-na \ --reason "Go-specific platform code, not needed in .NET" \ --db porting.db # Mark a feature as N/A dotnet run --project tools/NatsNet.PortTracker -- feature set-na \ --reason "Go signal handling, replaced by .NET host lifecycle" \ --db porting.db ``` ### Common N/A categories Items that typically do not need a .NET port: | Go item | Reason | |---------|--------| | `pse_darwin.go`, `pse_linux.go`, `pse_windows.go` | Go-specific platform syscall wrappers; use .NET `System.Diagnostics.Process` instead | | `disk_avail_windows.go`, `disk_avail_linux.go` | Go-specific disk APIs; use .NET `System.IO.DriveInfo` instead | | Custom logger (`logger.go`, `log.go`) | Replaced by Serilog via `ZB.MOM.NatsNet.Server.Host` | | Signal handling (`signal.go`) | Replaced by .NET Generic Host `IHostLifetime` | | Go `sync.Pool`, `sync.Map` wrappers | .NET has `ObjectPool`, `ConcurrentDictionary` built-in | | Build tags / `_test.go` helpers specific to Go test infra | Replaced by xUnit attributes and test fixtures | | `go:embed` directives | Replaced by embedded resources or `IFileProvider` | Every N/A must include a reason. Bare N/A status without explanation is not acceptable. ## Verification After mapping all items, run a quick check: ```bash # Count unmapped items (should be 0) dotnet run --project tools/NatsNet.PortTracker -- report summary --db porting.db # Review all modules — every row should show DotNet Project filled or status n_a dotnet run --project tools/NatsNet.PortTracker -- module list --db porting.db # Review N/A items to confirm they all have reasons dotnet run --project tools/NatsNet.PortTracker -- module list --status n_a --db porting.db dotnet run --project tools/NatsNet.PortTracker -- feature list --status n_a --db porting.db ``` ## Completion Criteria - [ ] [#25](https://gitea.dohertylan.com/dohertj2/natsnet/issues/25) Every module has `dotnet_project` and `dotnet_namespace` set, or status is `n_a` with a reason - [ ] [#26](https://gitea.dohertylan.com/dohertj2/natsnet/issues/26) Every feature has `dotnet_project`, `dotnet_class`, and `dotnet_method` set, or status is `n_a` with a reason - [ ] [#27](https://gitea.dohertylan.com/dohertj2/natsnet/issues/27) Every test has `dotnet_project`, `dotnet_class`, and `dotnet_method` set, or status is `n_a` with a reason - [ ] [#28](https://gitea.dohertylan.com/dohertj2/natsnet/issues/28) Naming follows PascalCase and the namespace hierarchy described above - [ ] [#29](https://gitea.dohertylan.com/dohertj2/natsnet/issues/29) No two features map to the same class + method combination (collisions) - [ ] Close the Phase 4 milestone in Gitea: ```bash curl -s -X PATCH "https://gitea.dohertylan.com/api/v1/repos/dohertj2/natsnet/milestones/4" \ -H "Content-Type: application/json" \ -H "Authorization: token $GITEA_TOKEN" \ -d '{"state":"closed"}' ``` Or close it via the Gitea web UI at https://gitea.dohertylan.com/dohertj2/natsnet/milestone/4 ## Related Documentation - [Phase 3: Library Mapping](phase-3-library-mapping.md) -- library mappings inform .NET class choices - [Phase 5: Mapping Verification](phase-5-mapping-verification.md) -- next phase, validates all mappings - [Phase 6: Porting](phase-6-porting.md) -- uses these mappings as the implementation blueprint