Files
natsnet/docs/plans/phases/phase-4-dotnet-design.md
2026-02-26 06:23:13 -05:00

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 goroutine launches map to Task-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_project and dotnet_namespace set, or status is n_a with a reason
  • Every feature has dotnet_project, dotnet_class, and dotnet_method set, or status is n_a with a reason
  • Every test has dotnet_project, dotnet_class, and dotnet_method set, or status is n_a with a reason
  • Naming follows PascalCase and the namespace hierarchy described above
  • No two features map to the same class + method combination (collisions)