Skip to content

Instantly share code, notes, and snippets.

@blinkinglight
Created February 26, 2026 14:25
Show Gist options
  • Select an option

  • Save blinkinglight/c393c0e3266c7ab1174ed72224aa8c94 to your computer and use it in GitHub Desktop.

Select an option

Save blinkinglight/c393c0e3266c7ab1174ed72224aa8c94 to your computer and use it in GitHub Desktop.

Microservice Specification

Name

Echo Service

Purpose

The Echo Service is a simple microservice that receives any data payload via NATS messaging and returns the exact same data payload in its response.

Requirements

  1. NATS Integration

    • The service must connect to a NATS server.
    • It must subscribe to a specific subject (e.g., echo.request).
    • Upon receiving a message, it must publish the same message back to a reply subject provided by the client (e.g., client.reply.subject).
  2. Response Format

    • The service must return the received data unchanged.
    • If the incoming message has a header (nats.Header), the response must include the same headers.
  3. Error Handling

    • If an error occurs during processing or publishing, the service should log the error but not modify the original message content.
  4. Performance

    • The service should handle multiple concurrent requests efficiently.
    • Latency should be minimized without compromising correctness.
  5. Documentation

    • Provide usage documentation for connecting clients and expected behavior.

Non-Functional Requirements

  • Language/Framework: Go
  • Testing Framework: Go’s standard testing package + table-driven tests.
  • Dependency Management: Use Go modules.
  • Logging: Basic stdlib logging or use a minimal wrapper around it.
  • Code Quality: Follow clean architecture principles; prefer interfaces over concrete types where possible.

Interfaces Design

Ports & Adapters Architecture

Core Port

Define a core port interface that specifies the business logic contract.

package echo

type ServicePort interface {
	Echo(ctx context.Context, msg *nats.Msg) error
}

Application Layer

Implement the core port using application services.

package echo

import (
	"context"
	"log"
)

type AppService struct {
	port ServicePort
}

func NewAppService(port ServicePort) *AppService {
	return &AppService{port: port}
}

func (s *AppService) HandleRequest(ctx context.Context, msg *nats.Msg) error {
	return s.port.Echo(ctx, msg)
}

Infrastructure Layer

Provide the infrastructure implementation of the port, which includes NATS integration.

package echo

import (
	"context"
	"fmt"
	"github.com/nats-io/nats.go"
)

type NATSPort struct {
	nc *nats.Conn
}

func NewNATSPort(nc *nats.Conn) *NATSPort {
	return &NATSPort{nc: nc}
}

func (p *NATSPort) Echo(ctx context.Context, msg *nats.Msg) error {
	return p.nc.Publish(msg.Reply, msg.Data)
}

Test-Driven Development (TDD)

Unit Tests for Core Logic

First, write unit tests for the application layer.

package echo_test

import (
	"context"
	"testing"

	echo "your-package-name/echo"
)

func TestAppService_HandleRequest(t *testing.T) {
	ctx := context.Background()
	msg := &nats.Msg{
		Data: []byte("hello"),
		Reply: "reply.subject",
	}

	tests := []struct {
		name    string
		wantErr bool
	}{
		{"should pass when echo succeeds", false},
		{"should fail if echo fails", true},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			portMock := &echo.ServicePortMock{
				EchoFunc: func(ctx context.Context, msg *nats.Msg) error {
					if tt.wantErr {
						return fmt.Errorf("forced error")
					}
					return nil
				},
			}

			app := echo.NewAppService(portMock)
			err := app.HandleRequest(ctx, msg)
			if (err != nil) != tt.wantErr {
				t.Errorf("HandleRequest() error = %v, wantErr %v", err, tt.wantErr)
				return
			}
		})
	}
}

Mock Implementation for Dependency Injection

Create a mock for the service port.

package echo

type ServicePortMock struct {
	EchoFunc func(ctx context.Context, msg *nats.Msg) error
}

func (m *ServicePortMock) Echo(ctx context.Context, msg *nats.Msg) error {
	return m.EchoFunc(ctx, msg)
}

Integration Tests for NATS

Next, write integration tests verifying NATS connectivity and message round-trip.

package echo_test

import (
	"context"
	"encoding/json"
	"testing"

	"github.com/nats-io/nats.go"
	echo "your-package-name/echo"
	natsutil "github.com/nats-io/nats.go/testutils"
)

func TestNATSPort_Echo(t *testing.T) {
	s, opts := natsutil.CreateServerWithConf(nats.DefaultTestOptions)
	defer s.Stop()

	nc, _ := nats.Connect(opts.URI)
	defer nc.Close()

	port := echo.NewNATSPort(nc)

	subj := "echo.request"
	reply := "client.reply.subject"

	request := map[string]interface{}{
		"message": "test",
	}

	expectedJSON, _ := json.Marshal(request)

	msg := &nats.Msg{
		Subject: subj,
		Reply:   reply,
		Data:    expectedJSON,
	}

	done := make(chan error, 1)
	go func() {
		done <- port.Echo(context.Background(), msg)
	}()

	receivedMsg := <-s.InboxSub("client.reply.")
	var receivedData map[string]interface{}
	json.Unmarshal(receivedMsg.Data, &receivedData)

	if string(receivedMsg.Data) != string(expectedJSON) {
		t.Errorf("Expected %s, got %s", expectedJSON, receivedMsg.Data)
	}

	if err := <-done; err != nil {
		t.Errorf("Unexpected error: %v", err)
	}
}

Code Implementation

Main Entry Point

Bootstrap the service.

package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"

	"github.com/nats-io/nats.go"
	echo "your-package-name/echo"
)

const (
	subject = "echo.request"
)

func main() {
	nc, err := nats.Connect(nats.DefaultURL)
	if err != nil {
		log.Fatalf("Failed to connect to NATS: %v", err)
	}
	defer nc.Close()

	app := echo.NewAppService(echo.NewNATSPort(nc))

	// Subscribe to messages
	_, err = nc.Subscribe(subject, func(msg *nats.Msg) {
		if err := app.HandleRequest(context.Background(), msg); err != nil {
			log.Printf("Error handling request: %v", err)
		}
	})
	if err != nil {
		log.Fatalf("Failed to subscribe: %v", err)
	}

	log.Println("Echo service started")

	// Graceful shutdown
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)

	sig := <-signals
	log.Printf("Received signal: %s, shutting down...", sig)
}

Core Implementation

As per the ports & adapters design above, no additional changes are needed here.

Application Layer

Already covered in the ports & adapters section.

Infrastructure Layer

Already covered in the ports & adapters section.


This completes the specification, design, TDD approach, and actual code implementation for the Echo Service using NATS, CQRS principles, Flowbite styling hints, and idiomatic Go practices.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment