golang使用六边形架构编写可维护代码指南插件库Hex Monscape的使用

Golang使用六边形架构编写可维护代码指南 - Hex Monscape插件库使用

Hex Monscape是一个基于Golang和Vue 3开发的回合制游戏项目,它展示了如何应用六边形架构(Hexagonal Architecture)来编写可维护的代码。

项目概述

Hex Monscape是Haraj Solutions团队用于新成员入职培训的有趣项目。通过这个游戏项目,团队展示了他们如何在生产代码中使用六边形架构,即使团队规模很小也能维护好服务数百万用户的系统。

hex_monscape_preview

六边形架构优势

六边形架构的主要优势在于:

  • 加快服务开发速度
  • 使代码可以被整个团队维护
  • 容易替换基础设施代码(如存储层)

游戏设计

在游戏中,玩家扮演一个10岁的怪物猎人,与怪物伙伴一起旅行,寻找并击败3只强大的野生怪物。

游戏流程如下:

Game Flow

战斗流程如下:

Battle Flow

如何使用Hex Monscape

在线体验

你可以直接访问在线版本: https://hex-monscape.haraj.app

本地运行

要本地运行游戏,需要先安装:

  • Docker v20.10.23或更高版本(包含Docker Compose v2.15.1)
  • make工具

然后运行:

make run

等待看到如下消息后,访问 http://localhost:8161 即可:

rest-memory-client-1  | yarn run v1.22.19
rest-memory-client-1  | $ vite --host --port 8161
rest-memory-client-1  |
rest-memory-client-1  |   vite v2.8.4 dev server running at:
rest-memory-client-1  |
rest-memory-client-1  |   > Local:    http://localhost:8161/
rest-memory-client-1  |   > Network:  http://172.31.0.3:8161/
rest-memory-client-1  |
rest-memory-client-1  |   ready in 151ms.

服务器变体

项目提供了3种不同的服务器实现,展示如何轻松替换基础设施:

  1. 使用内存存储:
make run-rest-memory
  1. 使用DynamoDB存储:
make run-rest-dynamodb
  1. 使用MySQL存储:
make run-rest-mysql

示例代码

以下是使用六边形架构的核心代码示例:

// 领域层 - 定义核心业务逻辑
package battle

// Battle是领域模型,代表一场战斗
type Battle struct {
    ID         string
    Player     *Monster
    Enemy      *Monster
    Turn       int
    Status     Status
    CreatedAt  time.Time
    FinishedAt *time.Time
}

// 领域服务接口
type Service interface {
    StartBattle(playerID, enemyID string) (*Battle, error)
    Attack(battleID string) (*Battle, error)
    GetBattle(battleID string) (*Battle, error)
}

// 基础设施层 - 实现存储接口
package storage

// BattleRepository定义了持久化接口
type BattleRepository interface {
    Save(battle *battle.Battle) error
    FindByID(id string) (*battle.Battle, error)
}

// 内存存储实现
type BattleMemoryStorage struct {
    battles map[string]*battle.Battle
    mu      sync.Mutex
}

func (s *BattleMemoryStorage) Save(b *battle.Battle) error {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.battles[b.ID] = b
    return nil
}

// 应用层 - 连接领域和基础设施
package app

type BattleApp struct {
    battleSvc battle.Service
}

func NewBattleApp(repo storage.BattleRepository) *BattleApp {
    return &BattleApp{
        battleSvc: battle.NewService(repo),
    }
}

func (a *BattleApp) StartBattle(playerID, enemyID string) (*battle.Battle, error) {
    return a.battleSvc.StartBattle(playerID, enemyID)
}

核心维护者

如有问题可以联系:

许可证

MIT


更多关于golang使用六边形架构编写可维护代码指南插件库Hex Monscape的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang使用六边形架构编写可维护代码指南插件库Hex Monscape的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用六边形架构编写可维护Go代码:Hex Monscape指南

六边形架构(Hexagonal Architecture)是一种将业务逻辑与外部依赖分离的设计模式,使应用程序更易于维护和测试。Hex Monscape是一个Go语言的插件库,帮助开发者实现六边形架构。

六边形架构核心概念

六边形架构的核心是将应用程序分为:

  • 内部核心:包含业务逻辑和领域模型
  • 外部适配器:处理与外部系统的交互(数据库、API、UI等)

Hex Monscape基本使用

安装

go get github.com/hexdigest/gowrap

基础示例

package main

import (
	"github.com/hexdigest/gowrap/hex"
)

// 定义核心业务接口
type UserService interface {
	CreateUser(name string, email string) (User, error)
	GetUser(id string) (User, error)
}

// 领域模型
type User struct {
	ID    string
	Name  string
	Email string
}

// 实现核心业务逻辑
type userServiceImpl struct{}

func (s *userServiceImpl) CreateUser(name string, email string) (User, error) {
	// 业务逻辑实现
	return User{
		ID:    "123",
		Name:  name,
		Email: email,
	}, nil
}

func (s *userServiceImpl) GetUser(id string) (User, error) {
	// 业务逻辑实现
	return User{
		ID:    id,
		Name:  "John Doe",
		Email: "john@example.com",
	}, nil
}

func main() {
	// 创建核心服务
	coreService := &userServiceImpl{}
	
	// 使用Hex Monscape包装核心服务
	hexService := hex.NewServiceWrapper(coreService)
	
	// 添加适配器
	httpAdapter := hex.NewHTTPAdapter(hexService)
	httpAdapter.Start(":8080")
}

高级用法

自定义适配器

package adapters

import (
	"encoding/json"
	"net/http"
	
	"github.com/hexdigest/gowrap/hex"
)

type JSONHTTPAdapter struct {
	service hex.Service
}

func NewJSONHTTPAdapter(service hex.Service) *JSONHTTPAdapter {
	return &JSONHTTPAdapter{service: service}
}

func (a *JSONHTTPAdapter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch r.URL.Path {
	case "/users":
		if r.Method == "POST" {
			var req struct {
				Name  string `json:"name"`
				Email string `json:"email"`
			}
			if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
				http.Error(w, err.Error(), http.StatusBadRequest)
				return
			}
			
			user, err := a.service.(UserService).CreateUser(req.Name, req.Email)
			if err != nil {
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			
			json.NewEncoder(w).Encode(user)
		}
	default:
		http.NotFound(w, r)
	}
}

依赖注入

package main

import (
	"github.com/hexdigest/gowrap/hex"
	"yourproject/adapters"
	"yourproject/core"
)

func main() {
	// 初始化核心服务
	userService := core.NewUserService()
	
	// 初始化存储库适配器
	userRepo := adapters.NewPostgresUserRepository()
	
	// 将适配器注入核心服务
	userService.SetRepository(userRepo)
	
	// 包装核心服务
	hexService := hex.NewServiceWrapper(userService)
	
	// 启动HTTP适配器
	httpAdapter := adapters.NewJSONHTTPAdapter(hexService)
	http.ListenAndServe(":8080", httpAdapter)
}

最佳实践

  1. 明确边界:严格区分核心业务逻辑和适配器代码
  2. 依赖倒置:适配器应该实现核心定义的接口,而不是反过来
  3. 单一职责:每个适配器只处理一种外部交互
  4. 测试策略
    • 核心逻辑使用单元测试
    • 适配器使用集成测试

测试示例

package core_test

import (
	"testing"
	
	"github.com/hexdigest/gowrap/hex"
	"yourproject/core"
	"yourproject/core/mocks"
)

func TestUserService(t *testing.T) {
	// 创建mock存储库
	mockRepo := new(mocks.UserRepository)
	mockRepo.On("Save", mock.Anything).Return(nil)
	
	// 创建服务实例
	service := core.NewUserService()
	service.SetRepository(mockRepo)
	
	// 包装服务
	hexService := hex.NewServiceWrapper(service)
	
	// 测试
	user, err := hexService.(core.UserService).CreateUser("test", "test@example.com")
	if err != nil {
		t.Errorf("Expected no error, got %v", err)
	}
	
	if user.Name != "test" {
		t.Errorf("Expected user name 'test', got '%s'", user.Name)
	}
	
	mockRepo.AssertExpectations(t)
}

Hex Monscape通过提供标准化的包装器和适配器接口,简化了六边形架构的实现过程。遵循这些模式可以显著提高Go应用程序的可维护性和可测试性。

回到顶部