ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Go项目架构演进:从脚本式到DDD四层架构的实战指南

Go项目架构演进:从脚本式到DDD四层架构的实战指南 1. 项目组织架构的十字路口从“能跑就行”到“清晰可维护”刚接触Go语言那会儿我写项目基本就是“脚本式”的一个main.go文件里塞满所有逻辑数据库操作、业务处理、HTTP路由全挤在一起。项目小的时候改起来飞快心里还挺得意。直到项目规模膨胀加个新功能要翻遍几千行代码修个Bug牵一发而动全身我才意识到代码的组织方式直接决定了项目的生命周期和团队的协作效率。今天我们就来聊聊Go项目里最常见的三种组织范式DDD四层架构、经典的MVC模式以及最原始的脚本式开发。这不是一个孰优孰劣的判断题而是一个“在什么场景下选择什么方案更合适”的思考题。无论你是刚入门的Gopher还是正在为团队技术选型纠结的Tech Lead理解这三种模式的本质、适用边界和迁移成本都能帮你做出更明智的决策避免在项目后期陷入重构的泥潭。2. 三种范式的本质剖析与核心差异在深入具体结构之前我们必须先理解这三种范式背后的哲学和设计目标。它们代表了三种不同的抽象层次和关注点分离的粒度。2.1 脚本式快速原型与个人工具的利器脚本式或者说“平铺直叙式”是很多Go新手包括当年的我最自然的写法。它的核心特征就是几乎没有显式的架构分层所有代码逻辑通常按执行顺序组织在一个或少数几个包package中。典型结构可能长这样/myapp ├── main.go ├── config.yaml └── utils.go (可能有一些辅助函数)在main.go里你可能会看到这样的代码流程package main import ( database/sql encoding/json fmt net/http _ github.com/go-sql-driver/mysql ) func main() { // 1. 读取配置 cfg : readConfig(config.yaml) // 2. 连接数据库 db, err : sql.Open(mysql, cfg.DSN) // ... 错误处理 // 3. 定义HTTP处理函数业务逻辑和数据库操作混在一起 http.HandleFunc(/user, func(w http.ResponseWriter, r *http.Request) { var req struct{ ID int } json.NewDecoder(r.Body).Decode(req) // 直接查询数据库 var name string err : db.QueryRow(SELECT name FROM users WHERE id ?, req.ID).Scan(name) if err ! nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } // 返回结果 json.NewEncoder(w).Encode(map[string]string{name: name}) }) // 4. 启动服务器 http.ListenAndServe(:8080, nil) }它的优势非常明显极致的开发速度想法能立刻转化为代码无需思考目录结构、接口定义。认知负担极低所有逻辑一目了然不需要在不同文件间跳转。依赖简单通常只需要标准库和少数几个直接依赖。但它的劣势在项目成长后会暴露无遗高度耦合数据库schema变动会直接冲击业务逻辑和API层。难以测试因为逻辑和外部依赖DB、HTTP紧耦合写单元测试需要大量mock甚至无法测试。无法复用业务逻辑被埋在HTTP处理函数中无法被其他入口如CLI命令、消息队列消费者使用。维护噩梦随着代码量增长它会变成一个“大泥球”任何修改都风险极高。实操心得脚本式并非一无是处。它非常适合一次性脚本、内部小工具、概念验证PoC项目或者学习某个新库时的实验代码。它的核心价值在于“快速验证想法”。但一旦你预见到这个代码的生命周期会超过两周或者有第二个人需要阅读它就应该考虑更结构化的方式了。2.2 MVC模式Web应用的经典蓝图MVCModel-View-Controller是Web开发领域经久不衰的架构模式其核心思想是将应用分为三个职责清晰的部件在Go的Web项目中通常对应为Model模型代表数据和业务规则。在Go中这常常是数据库表对应的结构体Struct和操作这些结构体的函数或方法。有时也包含一些核心业务逻辑。View视图负责数据的展示。在前后端分离的现代Web开发中“视图”通常由独立的前端项目负责后端仅提供JSON/XML等数据接口。因此在Go后端项目中“V”层可能弱化或转变为“Presenter”或“Serializer”负责将模型数据格式化为API响应。Controller控制器接收用户输入HTTP请求协调Model和View。它从请求中提取参数调用相应的Model进行业务处理然后选择View来渲染响应。一个典型的Go MVC项目目录可能如下/myapp ├── main.go ├── go.mod ├── go.sum ├── configs/ ├── controllers/ # 控制器层 │ ├── user_controller.go │ └── product_controller.go ├── models/ # 模型层 │ ├── user.go │ └── product.go ├── repositories/ # 数据访问层有时被归入Model │ ├── user_repo.go │ └── product_repo.go ├── services/ # 业务逻辑层有时也被归入Model或单独抽出 │ └── user_service.go ├── utils/ └── views/ # 在模板渲染项目中存在在API项目中可能没有或很薄 └── index.htmlMVC的核心价值在于关注点分离将输入控制Controller、业务逻辑与数据Model、输出渲染View分开使得代码更易于理解和维护。可测试性提升Controller可以相对独立地测试通过注入Mock的Service或Model业务逻辑集中在Service或Model中也便于测试。团队协作前端和后端开发者可以基于ModelAPI契约进行并行开发。然而传统的MVC在复杂业务系统下也会遇到挑战“胖控制器”或“胖模型”业务逻辑可能被随意地放在Controller或Model中导致这些层变得臃肿职责不清。模型贫血Model层可能退化为仅有数据字段定义和简单CRUD方法的“贫血模型”真正的业务规则散落在Service或Controller中破坏了面向对象设计的封装性。依赖方向模糊Controller依赖ModelModel又可能依赖全局的数据库连接这种隐式的、网状的依赖关系在大型项目中会变得难以管理。2.3 DDD与四层架构应对复杂业务系统的武器领域驱动设计Domain-Driven Design, DDD是一种应对复杂软件系统核心复杂性的方法论它强调以领域Domain为核心通过通用语言Ubiquitous Language将业务专家和开发人员连接起来并围绕领域模型进行软件设计。当DDD的思想落地到代码结构时常采用一种清晰的分层架构其中四层架构是一种经典实现。这四层从外到内依赖方向严格向内指向领域层用户接口层User Interface Layer / Presentation Layer对外提供访问方式如HTTP API、gRPC、GraphQL、CLI等。它负责接收请求进行基本的数据校验和格式转换然后调用应用服务最后将结果组装成响应格式。这一层很薄不应包含业务逻辑。应用层Application Layer协调领域对象完成一个特定的用户用例Use Case。它代表一个“事务脚本”负责任务编排、事务管理、权限校验等跨领域对象的协调工作。它本身也不包含核心业务规则只是指挥领域层干活。领域层Domain Layer这是系统的核心和灵魂。包含领域实体Entity、值对象Value Object、领域服务Domain Service、领域事件Domain Event和仓储接口Repository Interface。这里封装了最纯粹、最稳定的业务规则和逻辑。这一层应该完全独立于外部世界数据库、HTTP框架等只通过接口依赖基础设施。基础设施层Infrastructure Layer为其他层提供技术支持。实现领域层定义的仓储接口如用GORM操作MySQL提供消息队列发送、邮件发送、文件存储等具体实现。它是细节的封装层。一个遵循DDD四层思想的Go项目目录可能这样组织/myapp ├── cmd/ │ └── api/ │ └── main.go # 应用入口依赖注入的组装中心 ├── internal/ # 内部包外部项目无法导入 │ ├── app/ # 应用层 │ │ └── user/ │ │ ├── service.go # 应用服务CreateUserCommand, GetUserQuery │ │ └── dto.go # 数据传输对象 │ ├── domain/ # 领域层核心 │ │ └── user/ │ │ ├── entity.go # 用户实体包含业务方法和规则 │ │ ├── vo.go # 值对象如Email, Password │ │ ├── service.go # 领域服务当某个操作不属于单个实体时 │ │ ├── event.go # 领域事件UserRegisteredEvent │ │ └── repository.go # 仓储接口UserRepository │ └── infrastructure/ # 基础设施层 │ ├── persistence/ # 持久化实现 │ │ └── mysql/ │ │ └── user_repo.go # UserRepository的MySQL实现 │ ├── cache/ │ └── pkg/ # 一些内部共享的包如数据库客户端、日志配置 ├── pkg/ # 可供外部导入的公共库可选 │ └── errors/ └── api/ # API定义如OpenAPI Spec, Protobuf文件DDD四层架构的核心优势业务核心隔离与保护领域层是项目的“皇冠明珠”被外层严密保护不受技术细节数据库、Web框架污染。业务规则高度内聚。可测试性极佳领域层是纯Go代码不依赖任何外部组件单元测试编写轻松且运行飞快。其他层也可以通过依赖注入进行集成测试。技术细节可替换因为基础设施层通过接口为上层服务更换数据库从MySQL到PostgreSQL或Web框架从Gin到Echo对领域核心影响极小。适应复杂业务通过实体、值对象、领域事件等模式能更好地对复杂、多变的业务逻辑进行建模使代码结构更贴近业务语言。它的代价也很明显高认知与设计成本需要深入理解业务进行领域建模设计接口。前期设计时间更长。代码量增加分层、接口、DTO等会引入更多的文件和类型定义对于一个简单的CRUD应用来说显得“过度设计”。学习曲线陡峭团队成员需要理解DDD的概念和分层职责对新手不友好。3. 从MVC到DDD四层的渐进式演进路径很多团队并非从零开始一个DDD项目而是在维护一个逐渐变得臃肿的MVC项目时感受到了架构之痛。直接重写风险巨大渐进式重构是更可行的策略。下面以一个“用户注册”功能为例展示如何从MVC的“胖控制器”演变为清晰的四层结构。阶段一典型的MVC“胖控制器”// controllers/user_controller.go package controllers import ( net/http your-app/models your-app/utils ) type UserController struct { // 可能直接依赖全局DB或ORM } func (c *UserController) Register(w http.ResponseWriter, r *http.Request) { // 1. 参数绑定与校验本应在Controller层 var req struct { Email string json:email Password string json:password Name string json:name } if err : json.NewDecoder(r.Body).Decode(req); err ! nil {...} if !utils.ValidateEmail(req.Email) {...} // 2. 业务逻辑本应在Service/Domain层 // 检查邮箱是否已存在 var existingUser models.User if err : db.Where(email ?, req.Email).First(existingUser).Error; err nil { http.Error(w, email already exists, http.StatusBadRequest) return } // 密码加密业务规则 hashedPassword, err : utils.HashPassword(req.Password) if err ! nil {...} // 创建用户数据操作与业务逻辑混杂 user : models.User{ Email: req.Email, Password: hashedPassword, Name: req.Name, Status: pending, // 业务状态 } if err : db.Create(user).Error; err ! nil {...} // 3. 发送激活邮件基础设施调用混在业务中 go utils.SendActivationEmail(user.Email, user.ID) // 4. 返回响应 w.WriteHeader(http.StatusCreated) json.NewEncoder(w).Encode(map[string]interface{}{user_id: user.ID}) }问题一个函数里混合了参数校验、业务规则邮箱唯一性、密码加密、状态设置、数据持久化、外部服务调用发邮件和响应组装。难以测试无法复用。阶段二引入Service层剥离业务逻辑这是迈向清晰架构的第一步将核心业务逻辑从Controller移动到独立的Service中。// services/user_service.go package services import your-app/models type UserService struct { userRepo models.UserRepository // 假设我们定义了一个仓库接口 emailSender EmailSender } func (s *UserService) Register(email, password, name string) (*models.User, error) { // 业务逻辑集中在这里 if err : validateUserInput(email, password, name); err ! nil { return nil, err } // 通过接口调用解耦了具体数据库操作 if exists, err : s.userRepo.ExistsByEmail(email); err ! nil || exists { return nil, errors.New(email exists) } hashedPwd : hashPassword(password) // 业务规则 user : models.NewUser(email, hashedPwd, name) // 使用创建函数或方法封装创建逻辑 if err : s.userRepo.Save(user); err ! nil { return nil, err } // 触发事件或调用外部服务 if err : s.emailSender.SendWelcome(email); err ! nil { // 如何处理记录日志但可能不返回错误给用户 log.Printf(failed to send welcome email: %v, err) } return user, nil }此时Controller变薄了func (c *UserController) Register(w http.ResponseWriter, r *http.Request) { var req RegisterRequest // 只做参数绑定和基本格式校验 if err : c.ShouldBindJSON(req); err ! nil {...} // 调用Service user, err : c.userService.Register(req.Email, req.Password, req.Name) if err ! nil { c.JSON(http.StatusBadRequest, gin.H{error: err.Error()}) return } // 组装响应DTO resp : UserResponse{ID: user.ID, Name: user.Name} c.JSON(http.StatusCreated, resp) }改进业务逻辑集中到了ServiceController职责单一。但models.User可能还是贫血的且Service层仍然承担了过多协调职责。阶段三迈向DDD强化领域层这是关键一步我们将业务规则真正内化到领域实体中并明确分层。丰富领域模型Domain Layer// internal/domain/user/entity.go package user import ( errors regexp ) type User struct { ID int email Email // 值对象封装邮箱格式校验 passwordHash PasswordHash // 值对象封装密码哈希逻辑 name string status UserStatus } func NewUser(emailStr, plainPassword, name string) (*User, error) { email, err : NewEmail(emailStr) if err ! nil { return nil, err } pwdHash, err : NewPasswordHash(plainPassword) if err ! nil { return nil, err } if len(name) 0 { return nil, errors.New(name is required) } // 业务规则新用户初始状态为“待激活” return User{ email: email, passwordHash: pwdHash, name: name, status: StatusPending, }, nil } // 业务方法激活用户 func (u *User) Activate() error { if u.status ! StatusPending { return errors.New(user is not in pending status) } u.status StatusActive return nil } // 值对象示例 type Email struct { value string } func NewEmail(v string) (Email, error) { // 复杂的邮箱校验逻辑在这里 if !regexp.MustCompile(^[a-zA-Z0-9._%-][a-zA-Z0-9.-]\.[a-zA-Z]{2,}$).MatchString(v) { return Email{}, errors.New(invalid email format) } return Email{value: v}, nil } func (e Email) String() string { return e.value }定义清晰的仓储接口Domain Layer// internal/domain/user/repository.go package user type Repository interface { FindByID(id int) (*User, error) FindByEmail(email Email) (*User, error) Save(user *User) error // ExistsByEmail 这类判断业务状态的查询有时会放在领域服务中 }精简应用服务Application Layer 应用服务不再处理具体的业务规则如密码哈希而是协调领域对象和基础设施完成用例。// internal/app/user/service.go package app import ( context your-app/internal/domain/user ) type UserService struct { repo user.Repository eventBus EventBus // 用于发布领域事件 // 不再直接依赖EmailSender } type RegisterCommand struct { Email string Password string Name string } func (s *UserService) Register(ctx context.Context, cmd RegisterCommand) error { // 1. 通过仓储检查唯一性约束这可以视为一种业务规则守卫 existing, err : s.repo.FindByEmail(user.Email(cmd.Email)) if err ! nil !errors.Is(err, ErrNotFound) { return err // 系统错误 } if existing ! nil { return ErrEmailExists // 业务错误 } // 2. 使用领域工厂创建聚合根实体 newUser, err : user.NewUser(cmd.Email, cmd.Password, cmd.Name) if err ! nil { return err // 业务规则校验失败 } // 3. 持久化 if err : s.repo.Save(newUser); err ! nil { return err } // 4. 发布领域事件而非直接调用基础设施 s.eventBus.Publish(ctx, user.RegisteredEvent{ UserID: newUser.ID, Email: newUser.Email(), Timestamp: time.Now(), }) return nil }基础设施层实现细节Infrastructure Layer// internal/infrastructure/persistence/mysql/user_repo.go package mysql import ( gorm.io/gorm your-app/internal/domain/user ) type UserRepository struct { db *gorm.DB } func (r *UserRepository) Save(u *user.User) error { // 将领域实体转换为持久化模型PO po : toPersistenceModel(u) return r.db.Save(po).Error } func (r *UserRepository) FindByEmail(em user.Email) (*user.User, error) { var po UserPO if err : r.db.Where(email ?, em.String()).First(po).Error; err ! nil { return nil, err } return toDomainEntity(po), nil } // ... toPersistenceModel 和 toDomainEntity 负责领域模型与数据模型的转换用户接口层Presentation Layer 最终的Controller变得非常薄只负责协议适配。// internal/interfaces/http/user_handler.go package http type UserHandler struct { registerService app.RegisterUserService // 依赖应用服务接口 } func (h *UserHandler) Register(c *gin.Context) { var req RegisterRequest if err : c.ShouldBindJSON(req); err ! nil { c.JSON(400, gin.H{error: invalid request}) return } cmd : app.RegisterCommand{ Email: req.Email, Password: req.Password, Name: req.Name, } err : h.registerService.Register(c.Request.Context(), cmd) if err ! nil { // 将领域错误或应用错误映射为HTTP状态码和消息 handleError(c, err) return } c.Status(201) }通过这个渐进过程代码从高度耦合的状态演变为职责清晰、依赖明确、核心业务受到保护的结构。每一次演进都让代码更易于测试、理解和修改。4. 实战选型指南如何为你的项目选择架构了解了三种范式的本质和演进路径后面对一个新项目我们该如何选择下面这个决策框架或许能帮到你。4.1 评估项目的核心维度业务复杂度简单CRUD业务规则极少主要是数据的增删改查。例如后台管理系统的基础数据管理、简单的数据看板。中等复杂度包含一些工作流、状态机、业务规则校验。例如电商的购物车、订单流程涉及库存、优惠券、支付。高度复杂核心领域有大量且频繁变化的业务规则、复杂的业务概念和交互。例如金融交易系统、保险理赔系统、物流调度系统。团队规模与经验单人/小团队5人沟通成本低架构可以更灵活。中型团队5-15人需要清晰的模块边界和接口定义来并行开发。大型团队/多团队需要严格的架构规范和清晰的上下文边界在DDD中称为“限界上下文”。项目生命周期与变化预期短期/实验性项目快速验证想法可能很快被抛弃或重写。长期核心业务系统需要支撑公司业务多年发展对可维护性和扩展性要求极高。需求变化频率业务规则是否经常调整新的业务模式是否会不断加入性能与迭代速度要求需要快速上线和迭代对开发速度要求高于长期代码质量。对稳定性和性能有极高要求不能容忍因为架构混乱导致的线上事故或性能瓶颈。4.2 决策矩阵与建议基于以上维度我们可以得出一些一般性建议项目特征推荐架构理由与注意事项个人工具、一次性脚本、学习Demo、PoC脚本式目标明确生命周期短追求极致的开发速度。别在螺丝刀上装导弹瞄准系统。小型Web应用、内部管理后台、初创公司MVPMVC (可增强版)业务以CRUD为主复杂度不高。采用MVC能快速搭建清晰结构。建议从一开始就强制分离Controller、Service、Repository避免“胖控制器”。即使Model“贫血”一点也比全混在一起强。中型SaaS应用、电商平台后端、有明显业务规则的系统DDD四层架构 (精简版)业务逻辑开始变得复杂且重要。建议采用DDD的思想但不必追求完美的战术建模如聚合根、领域事件等用到极致。核心是建立清晰的领域层将业务规则封装进去并严格分层依赖。可以暂时忽略值对象、领域事件等高级概念先保证实体Entity的丰富性和仓储接口的清晰。大型金融核心系统、复杂企业ERP、微服务中的核心领域服务完整的DDD四层架构业务极其复杂是公司的核心竞争力。值得投入资源进行深入的领域建模划分限界上下文并完整应用DDD的战术模式。架构的清晰度和稳定性优先级最高。团队DDD经验不足但项目复杂度在增长从MVC向DDD渐进重构不要试图一步到位。按照第3部分的演进路径先从MVC中抽出Service层然后逐步丰富领域模型最后引入明确的层间依赖和接口。每次重构一小部分持续改进。注意事项架构的终极目标是控制复杂度而不是增加复杂度。如果采用一个过于复杂的架构来管理一个简单的系统那么架构本身就成为了最大的复杂性问题。始终牢记最简单的可行架构就是最好的架构。4.3 Go语言特性对架构选择的影响Go语言本身的一些特性也影响了这些架构模式的实现简洁性与显式性Go没有传统的类和继承推崇组合和接口。这使得DDD中“通过接口进行依赖倒置”的模式非常自然。同时显式的错误处理要求我们在设计应用层服务时仔细考虑错误分类领域错误、基础设施错误、验证错误。包Package管理Go的internal目录是实践清晰架构的利器它可以防止领域层等内部包被项目外部的代码意外导入强制了依赖方向。依赖注入DIGo没有原生的DI框架通常通过构造函数注入手动实现。这在四层架构中尤为重要需要在main.go或专门的wire.go如果使用Google Wire等工具中完成所有组件的组装。这虽然有些繁琐但让依赖关系变得极其清晰。错误处理在DDD架构中需要区分领域错误如“邮箱已存在”、“库存不足”和系统错误如“数据库连接失败”。领域错误是业务逻辑的一部分应该被定义在领域层并能在应用层被处理并转化为用户友好的消息。5. 常见陷阱、问题排查与最佳实践即使选对了方向在实施过程中也会遇到各种坑。以下是一些常见问题及解决方案。5.1 分层架构中的典型“反模式”领域层依赖外部库问题在domain/user/entity.go中import “gorm.io/gorm”用于定义GORM标签。这污染了领域层。解决领域实体应该是纯Go结构体。持久化细节如GORM标签、表名应在基础设施层的持久化模型PO中定义。使用转换函数如前面例子中的toPersistenceModel在仓储实现中进行实体与PO的互相转换。应用服务变成“上帝类”问题所有业务逻辑都写在了应用服务Application Service里领域实体退化为仅有getter/setter的数据容器。解决牢记“应用服务协调领域实体执行业务规则”。将属于单个实体状态的变更如user.ChangePassword()和核心计算规则封装在实体方法中。应用服务只负责调用多个实体或领域服务管理事务边界。过度抽象与接口爆炸问题为每一个简单的仓储或服务都定义一个接口导致接口过多增加认知负担。解决按需定义接口。通常只为那些确实可能有多种实现如Repository可能有MySQL、内存、测试实现或需要被Mock测试的依赖定义接口。对于几乎不可能换掉的内部组件可以直接依赖具体类型。循环依赖问题在Go中包之间不能循环导入。如果domain/user包引用了domain/order而domain/order又引用了domain/user就会编译失败。解决重新审视领域边界这两个实体是否真的属于同一个聚合或许Order中只需要持有UserID而不是整个User对象。使用领域服务将涉及多个实子的复杂逻辑提取到一个独立的领域服务中该服务可以导入所需的多个领域包。依赖倒置在其中一个领域包中定义接口另一个包通过接口依赖它实现放在基础设施层或应用层。5.2 测试策略的调整不同的架构测试策略也不同。脚本式几乎只能做端到端E2E或集成测试难以做单元测试。MVC可以对Service层进行单元测试Mock掉Repository对Controller进行集成测试。DDD四层领域层单元测试这是最宝贵、运行最快的测试。因为领域层不依赖任何外部东西你可以轻松测试实体、值对象的所有业务方法和规则。重点覆盖。应用服务集成测试使用容器化的测试数据库如testcontainers-go或内存数据库测试应用服务与真实仓储的集成验证用例流程是否正确。API端到端测试针对重要的用户旅程如注册、下单编写E2E测试确保从API到数据库的整个链条畅通。但这类测试较慢不宜过多。5.3 性能与可维护性的平衡有人担心DDD多层转换如DTO-Entity-PO会影响性能。在绝大多数业务系统中这带来的微秒级开销与网络IO、数据库查询相比微不足道。可维护性带来的长期收益远大于这点性能损失。对于真正性能敏感的瓶颈点如热点接口可以通过缓存、更高效的查询等方式优化而不是牺牲架构的清晰度。5.4 项目启动与目录结构建议对于决定采用DDD四层架构的新项目我建议的启动步骤从领域开始与业务专家沟通识别核心子域定义最初的领域模型实体、值对象。先写在文档或白板上不要急于编码。定义仓储接口在领域包中为每个聚合根定义所需的Repository接口。思考需要哪些查询和持久化方法。编写领域层单元测试针对你定义的核心业务规则先写测试。这能帮你验证模型设计是否合理。实现基础设施层用最简单的方式如内存Map实现Repository接口让测试通过。实现应用服务编排领域对象实现第一个用户用例如“注册用户”。实现用户接口层用你熟悉的Web框架暴露HTTP API。最后组装在main.go中将所有组件仓储的具体实现、应用服务、控制器通过依赖注入组装起来。关于目录结构没有绝对标准。除了前面展示的按层划分internal/app,internal/domain也可以按功能模块划分每个模块内再分层/internal /module_a /domain /application /infrastructure /interfaces /module_b /domain /application ...这种方式在微服务或模块化单体中更清晰模块间耦合度更低。最后记住架构是演进而来的不是设计出来的。从一个清晰简单的MVC开始随着业务复杂度的提升敏锐地识别出代码的“坏味道”如重复的逻辑、难以测试的函数、经常一起变化的文件然后运用DDD的思想和模式进行局部重构逐步向更清晰的架构靠拢这才是可持续的工程实践。最危险的不是一开始用了简单的架构而是当复杂度来临时对架构的腐化视而不见。
返回列表