7.6 KiB
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
Solution Structure
Define the .NET solution layout following standard conventions:
src/
NATS.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
...
NATS.Server.Host/ # Host/entry point (Program.cs, DI, config)
tests/
NATS.Server.Tests/ # Unit tests for NATS.Server
Protocol/
Subscriptions/
JetStream/
...
NATS.Server.IntegrationTests/ # Cross-module and end-to-end tests
The NATS.Server project holds all portable logic. NATS.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 | NATS.Server.[Module] |
NATS.Server.Protocol, NATS.Server.Subscriptions |
| Test classes | [ClassName]Tests |
NatsParserTests, SubListTests |
| Test methods | [Method]_[Scenario]_[Expected] |
TryParse_ValidInput_ReturnsTrue |
| Interfaces | I[Name] |
IMessageRouter, ISubListAccess |
| Projects | NATS.Server[.Suffix] |
NATS.Server, NATS.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.
# 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 <id> \
--project "NATS.Server" \
--namespace "NATS.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 |
NATS.Server.Protocol |
server/sublist.go |
NATS.Server.Subscriptions |
server/jetstream*.go |
NATS.Server.JetStream |
server/route.go, server/gateway.go |
NATS.Server.Cluster |
server/auth.go, server/accounts.go |
NATS.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:
# List features for a specific module
dotnet run --project tools/NatsNet.PortTracker -- feature list --module <module_id> --db porting.db
# Map a feature
dotnet run --project tools/NatsNet.PortTracker -- feature map <id> \
--project "NATS.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
goroutinelaunches map toTask-based async methods
Step 3: Map tests
For each test function, assign the .NET test class and method:
# List tests for a module
dotnet run --project tools/NatsNet.PortTracker -- test list --module <module_id> --db porting.db
# Map a test
dotnet run --project tools/NatsNet.PortTracker -- test map <id> \
--project "NATS.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].
Step 4: Mark N/A items
Some Go code has no .NET equivalent. Mark these with a clear reason:
# Mark a module as N/A
dotnet run --project tools/NatsNet.PortTracker -- module set-na <id> \
--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 <id> \
--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 NATS.Server.Host |
Signal handling (signal.go) |
Replaced by .NET Generic Host IHostLifetime |
Go sync.Pool, sync.Map wrappers |
.NET has ObjectPool<T>, ConcurrentDictionary<K,V> 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:
# 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
- Every module has
dotnet_projectanddotnet_namespaceset, or status isn_awith a reason - Every feature has
dotnet_project,dotnet_class, anddotnet_methodset, or status isn_awith a reason - Every test has
dotnet_project,dotnet_class, anddotnet_methodset, or status isn_awith a reason - Naming follows PascalCase and the namespace hierarchy described above
- No two features map to the same class + method combination (collisions)
Related Documentation
- Phase 3: Library Mapping -- library mappings inform .NET class choices
- Phase 5: Mapping Verification -- next phase, validates all mappings
- Phase 6: Porting -- uses these mappings as the implementation blueprint