Skip to content

Feature/architecture modularization and ecs #45

Merged
GeWuYou merged 5 commits into
mainfrom
feature/architecture-modularization-and-ecs-
Feb 23, 2026
Merged

Feature/architecture modularization and ecs #45
GeWuYou merged 5 commits into
mainfrom
feature/architecture-modularization-and-ecs-

Conversation

@GeWuYou
Copy link
Copy Markdown
Owner

@GeWuYou GeWuYou commented Feb 23, 2026

Summary by Sourcery

Introduce a modular service module system for core architecture services and make ECS support configurable via architecture properties and modules.

New Features:

  • Add a pluggable IServiceModule and IServiceModuleManager abstraction with built-in modules for event bus, command executor, query executors, and ECS.
  • Introduce ArchitectureProperties.EnableEcs to toggle registration of ECS-related services via the module system.

Enhancements:

  • Refactor ArchitectureServices to resolve core services from the DI container via the new ServiceModuleManager instead of constructing them directly.
  • Adjust Architecture and ArchitectureContext lifecycle to register, initialize, and destroy service modules as part of architecture initialization and teardown.
  • Simplify ECS world access in ArchitectureContext to resolve IEcsWorld from the container, removing manual ECS initialization logic.

Tests:

  • Update architecture and ECS tests to reflect module-based service registration, ECS configuration via EnableEcs, and to verify service isolation and availability across multiple ArchitectureServices instances.

- 将架构服务重构为模块化设计,引入ServiceModuleManager统一管理
- 新增EventBusModule、CommandExecutorModule、QueryExecutorModule等核心服务模块
- 实现ECS模块支持,可配置启用Entity Component System功能
- 在架构初始化过程中集成模块注册、初始化和销毁流程
- 更新架构属性配置,添加EnableEcs开关控制ECS功能启用
- 优化服务获取方式,从直接依赖改为通过容器动态获取
- 移除架构上下文中的ECS相关实现代码,统一由ECS模块管理
- 添加了对ArchitectureProperties和GFramework.Core.services的引用
- 实现了RegisterBuiltInServices方法用于注册内置服务
- 修改了构造函数测试以验证容器初始化而非所有服务
- 更新了EventBus、CommandExecutor和QueryExecutor的测试以验证注册后可用性
- 添加了AsyncQueryExecutor可用性测试
- 添加了未注册服务时EventBus为null的测试
- 在多个实例测试中添加了模块注册以确保独立性
- 添加了ModuleManager属性非空测试
- 实现了ECS配置开关控制模块注册的测试
- 移除了TestArchitectureContextV3中的硬编码服务实现
- 更新了ECS相关测试以直接注册EcsWorld到容器中
- 改进了ECS世界获取失败时的错误消息
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Feb 23, 2026

Reviewer's Guide

Introduces a modular service architecture with a ServiceModuleManager and pluggable IServiceModule implementations (including an ECS module), wires Architecture and ArchitectureServices to use these modules based on ArchitectureProperties (notably EnableEcs), and updates tests to reflect container-based resolution and ECS configuration-driven behavior.

Sequence diagram for architecture initialization with service modules and ECS

sequenceDiagram
    actor App
    participant Architecture
    participant ArchitectureServices
    participant ServiceModuleManager
    participant Container as IIocContainer
    participant Environment
    participant Module as IServiceModule

    App->>Architecture: InitializeAsync()
    activate Architecture
    Architecture->>Architecture: InitializeInternalAsync(asyncMode)
    activate Architecture

    Architecture->>ArchitectureServices: get Services
    Architecture->>ArchitectureServices: get Container
    Architecture->>Architecture: get Configuration.ArchitectureProperties

    Architecture->>ServiceModuleManager: RegisterBuiltInModules(Container, ArchitectureProperties)
    activate ServiceModuleManager
    ServiceModuleManager->>ServiceModuleManager: RegisterModule(EventBusModule)
    ServiceModuleManager->>ServiceModuleManager: RegisterModule(CommandExecutorModule)
    ServiceModuleManager->>ServiceModuleManager: RegisterModule(QueryExecutorModule)
    ServiceModuleManager->>ServiceModuleManager: RegisterModule(AsyncQueryExecutorModule)

    alt EnableEcs is true
        ServiceModuleManager->>ServiceModuleManager: RegisterModule(EcsModule(enabled:true))
    else EnableEcs is false
        ServiceModuleManager->>ServiceModuleManager: skip EcsModule
    end

    ServiceModuleManager->>ServiceModuleManager: sort modules by Priority

    loop for each enabled module
        ServiceModuleManager->>Module: Register(Container)
        Module->>Container: register services
    end
    deactivate ServiceModuleManager

    Architecture->>Environment: Initialize()

    Architecture->>Container: ExecuteServicesHook(Configurator)

    Architecture->>ServiceModuleManager: InitializeAllAsync(asyncMode)
    activate ServiceModuleManager
    loop for each enabled module
        alt asyncMode and Module is IAsyncInitializable
            ServiceModuleManager->>Module: InitializeAsync()
            Module-->>ServiceModuleManager: completed
        else
            ServiceModuleManager->>Module: Initialize()
        end
    end
    deactivate ServiceModuleManager

    Architecture-->>App: initialization completed
    deactivate Architecture
Loading

Class diagram for modular service architecture and modules

classDiagram
    direction LR

    class Architecture {
        -ArchitectureServices Services
        -IIocContainer Container
        -ArchitectureConfiguration Configuration
        -IEnvironment Environment
        +ValueTask InitializeAsync()
        -Task InitializeInternalAsync(bool asyncMode)
        +ValueTask DestroyAsync()
    }

    class ArchitectureServices {
        -IServiceModuleManager _moduleManager
        -IArchitectureContext _context
        +ArchitectureServices()
        +IIocContainer Container
        +IServiceModuleManager ModuleManager
        +IEventBus EventBus
        +ICommandExecutor CommandExecutor
        +IQueryExecutor QueryExecutor
        +IAsyncQueryExecutor AsyncQueryExecutor
        +void SetContext(IArchitectureContext context)
        +IArchitectureContext GetContext()
    }

    class IServiceModuleManager {
        +void RegisterModule(IServiceModule module)
        +void RegisterBuiltInModules(IIocContainer container, ArchitectureProperties properties)
        +IReadOnlyList~IServiceModule~ GetModules()
        +Task InitializeAllAsync(bool asyncMode)
        +ValueTask DestroyAllAsync()
    }

    class ServiceModuleManager {
        -ILogger _logger
        -List~IServiceModule~ _modules
        +void RegisterModule(IServiceModule module)
        +void RegisterBuiltInModules(IIocContainer container, ArchitectureProperties properties)
        +IReadOnlyList~IServiceModule~ GetModules()
        +Task InitializeAllAsync(bool asyncMode)
        +ValueTask DestroyAllAsync()
    }

    class IServiceModule {
        <<interface>>
        +string ModuleName
        +int Priority
        +bool IsEnabled
        +void Register(IIocContainer container)
        +void Initialize()
        +ValueTask DestroyAsync()
    }

    class EventBusModule {
        +string ModuleName
        +int Priority
        +bool IsEnabled
        +void Register(IIocContainer container)
        +void Initialize()
        +ValueTask DestroyAsync()
    }

    class CommandExecutorModule {
        +string ModuleName
        +int Priority
        +bool IsEnabled
        +void Register(IIocContainer container)
        +void Initialize()
        +ValueTask DestroyAsync()
    }

    class QueryExecutorModule {
        +string ModuleName
        +int Priority
        +bool IsEnabled
        +void Register(IIocContainer container)
        +void Initialize()
        +ValueTask DestroyAsync()
    }

    class AsyncQueryExecutorModule {
        +string ModuleName
        +int Priority
        +bool IsEnabled
        +void Register(IIocContainer container)
        +void Initialize()
        +ValueTask DestroyAsync()
    }

    class EcsModule {
        -EcsSystemRunner _ecsRunner
        -EcsWorld _ecsWorld
        +EcsModule(bool enabled)
        +string ModuleName
        +int Priority
        +bool IsEnabled
        +void Register(IIocContainer container)
        +void Initialize()
        +ValueTask DestroyAsync()
        +IEcsWorld GetEcsWorld()
        +EcsSystemRunner GetEcsRunner()
    }

    class ArchitectureProperties {
        +bool AllowLateRegistration
        +bool StrictPhaseValidation
        +bool EnableEcs
    }

    class ArchitectureContext {
        -IIocContainer _container
        -Dictionary~Type,object~ _serviceCache
        +ArchitectureContext(IIocContainer container)
        +TService GetService~TService~()
        +IMediator GetMediator()
        +IEcsWorld GetEcsWorld()
        +void RegisterEcsSystem~T~()
    }

    class IIocContainer {
        <<interface>>
        +void Register~TService~(TService instance)
        +void RegisterPlurality~TService~(TService instance)
        +TService Get~TService~()
        +bool Contains~TService~()
        +void Clear()
    }

    class IEventBus {
        <<interface>>
    }

    class ICommandExecutor {
        <<interface>>
    }

    class IQueryExecutor {
        <<interface>>
    }

    class IAsyncQueryExecutor {
        <<interface>>
    }

    class IEcsWorld {
        <<interface>>
    }

    class EcsWorld {
        +int EntityCount
        +void Dispose()
    }

    class EcsSystemRunner {
    }

    Architecture --> ArchitectureServices : uses
    ArchitectureServices --> IIocContainer : has
    ArchitectureServices --> IServiceModuleManager : uses
    ArchitectureServices ..> IEventBus : resolves from Container
    ArchitectureServices ..> ICommandExecutor : resolves from Container
    ArchitectureServices ..> IQueryExecutor : resolves from Container
    ArchitectureServices ..> IAsyncQueryExecutor : resolves from Container

    ServiceModuleManager ..|> IServiceModuleManager
    ServiceModuleManager o--> IServiceModule : manages modules

    EventBusModule ..|> IServiceModule
    CommandExecutorModule ..|> IServiceModule
    QueryExecutorModule ..|> IServiceModule
    AsyncQueryExecutorModule ..|> IServiceModule
    EcsModule ..|> IServiceModule

    ServiceModuleManager --> ArchitectureProperties : uses
    ServiceModuleManager --> IIocContainer : registers services
    ServiceModuleManager --> EcsModule : conditionally creates

    EcsModule --> EcsWorld : creates
    EcsModule ..> IEcsWorld : registers
    EcsModule --> EcsSystemRunner : resolves and manages

    Architecture --> ArchitectureProperties : uses in Configuration
    Architecture --> ServiceModuleManager : via Services.ModuleManager

    ArchitectureContext --> IIocContainer : uses
    ArchitectureContext ..> IEcsWorld : resolves
Loading

File-Level Changes

Change Details Files
Refactor ArchitectureServices to resolve core services from DI and expose a service module manager instead of constructing services directly.
  • Replace concrete EventBus/CommandExecutor/QueryExecutor/AsyncQueryExecutor fields with DI-resolved properties from IIocContainer.
  • Instantiate a ServiceModuleManager inside ArchitectureServices and expose it via a ModuleManager property.
  • Remove direct registration of core services in the ArchitectureServices constructor, leaving container initialization to modules.
GFramework.Core/architecture/ArchitectureServices.cs
Introduce a generic service module system and built-in service modules, including configurable ECS support.
  • Add IServiceModule and IServiceModuleManager abstractions for registering, initializing, and destroying service modules.
  • Implement ServiceModuleManager to manage module lifecycle, register built-in modules based on ArchitectureProperties (including EnableEcs), and coordinate async initialization/destruction.
  • Add EventBusModule, CommandExecutorModule, QueryExecutorModule, and AsyncQueryExecutorModule to register core services into the container by priority.
  • Add an EcsModule that creates/registers EcsWorld and EcsSystemRunner, implements lifecycle hooks, and exposes accessors for tests/internal use.
  • Extend ArchitectureProperties with an EnableEcs flag that controls ECS module registration.
GFramework.Core.Abstractions/architecture/IServiceModule.cs
GFramework.Core.Abstractions/architecture/IServiceModuleManager.cs
GFramework.Core.Abstractions/properties/ArchitectureProperties.cs
GFramework.Core/services/ServiceModuleManager.cs
GFramework.Core/services/modules/EventBusModule.cs
GFramework.Core/services/modules/CommandExecutorModule.cs
GFramework.Core/services/modules/QueryExecutorModule.cs
GFramework.Core/services/modules/AsyncQueryExecutorModule.cs
GFramework.Core/services/modules/EcsModule.cs
Wire Architecture to use ArchitectureServices as a concrete type and integrate service module registration/initialization into the lifecycle.
  • Change Architecture.Services to be a concrete ArchitectureServices instance (casting from the optional IArchitectureServices parameter).
  • During initialization, call Services.ModuleManager.RegisterBuiltInModules using the ArchitectureProperties configuration, then InitializeAllAsync after DI hooks.
  • During destruction, call Services.ModuleManager.DestroyAllAsync before clearing the container to ensure module cleanup.
GFramework.Core/architecture/Architecture.cs
Decouple ECS world management from ArchitectureContext and make ECS availability container- and configuration-driven.
  • Remove internal EcsWorld/EcsSystemRunner fields and InitializeEcs/DisposeEcs from ArchitectureContext, relying on IEcsWorld resolution via the container.
  • Update GetEcsWorld to resolve IEcsWorld via the service cache and throw an InvalidOperationException with a new message if ECS is not configured.
  • Keep RegisterEcsSystem using container.RegisterPlurality to remain compatible with the new ECS module setup.
GFramework.Core/architecture/ArchitectureContext.cs
Update tests to reflect module-based service registration and ECS configuration behavior.
  • Adjust ArchitectureServices tests to no longer expect services initialized in the constructor, instead requiring RegisterBuiltInModules to make EventBus/CommandExecutor/QueryExecutor/AsyncQueryExecutor available and asserting they are null otherwise.
  • Ensure multiple ArchitectureServices instances get independent core services after registering modules, and add a ModuleManager non-null assertion.
  • Add a test to verify EnableEcs toggles ECS module registration by comparing module counts with and without ECS enabled.
  • Update ECS tests to manually register EcsWorld/IEcsWorld in the container instead of calling InitializeEcs, and adjust exception messages for GetEcsWorld without initialization.
GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs
GFramework.Core.Tests/ecs/EcsAdvancedTests.cs
GFramework.Core.Tests/ecs/EcsIntegrationTests.cs
GFramework.Core.Tests/ecs/EcsBasicTests.cs
GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue, and left some high level feedback:

  • Accessors like EventBus, CommandExecutor, QueryExecutor, and AsyncQueryExecutor now call Container.Get<T>()! and will throw if built-in modules were not registered; consider either enforcing module registration in the ArchitectureServices constructor or failing with a clearer, intentional exception when the service is missing.
  • ServiceModuleManager.RegisterBuiltInModules is not idempotent with respect to the container: repeated calls will re-run module.Register(container) for already-registered modules; if this method can be invoked more than once per container, consider tracking whether registration has already occurred or guarding against duplicate registrations.
  • In Architecture, the Services field has been narrowed from IArchitectureServices to the concrete ArchitectureServices to access ModuleManager; to preserve abstraction, consider exposing ModuleManager on IArchitectureServices instead of downcasting to the concrete type.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Accessors like `EventBus`, `CommandExecutor`, `QueryExecutor`, and `AsyncQueryExecutor` now call `Container.Get<T>()!` and will throw if built-in modules were not registered; consider either enforcing module registration in the `ArchitectureServices` constructor or failing with a clearer, intentional exception when the service is missing.
- `ServiceModuleManager.RegisterBuiltInModules` is not idempotent with respect to the container: repeated calls will re-run `module.Register(container)` for already-registered modules; if this method can be invoked more than once per container, consider tracking whether registration has already occurred or guarding against duplicate registrations.
- In `Architecture`, the `Services` field has been narrowed from `IArchitectureServices` to the concrete `ArchitectureServices` to access `ModuleManager`; to preserve abstraction, consider exposing `ModuleManager` on `IArchitectureServices` instead of downcasting to the concrete type.

## Individual Comments

### Comment 1
<location> `GFramework.Core/architecture/Architecture.cs:71` </location>
<code_context>
-    ///     获取架构服务对象
-    /// </summary>
-    private IArchitectureServices Services { get; } = services ?? new ArchitectureServices();
+    private ArchitectureServices Services { get; } = services as ArchitectureServices ?? new ArchitectureServices();

     /// <summary>
</code_context>

<issue_to_address>
**issue (bug_risk):** Casting `services` to `ArchitectureServices` silently ignores non-concrete implementations of `IArchitectureServices`.

Because the constructor accepts `IArchitectureServices?` but the property is now `ArchitectureServices` and initialized via `services as ArchitectureServices ?? new ArchitectureServices()`, any custom `IArchitectureServices` implementation will be ignored and replaced with a new `ArchitectureServices`. This breaks the abstraction and can change caller behavior unexpectedly. Either change the parameter type to `ArchitectureServices?` if a concrete type is required, or keep the property as `IArchitectureServices` and store/use the instance as provided.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread GFramework.Core/architecture/Architecture.cs Outdated
- 将 ArchitectureServices 类型改为 IArchitectureServices 接口
- 简化 IArchitectureServices 接口中属性声明的访问修饰符
- 为 IArchitectureServices 接口添加模块管理器属性
- 为接口添加总体功能描述注释
- 为RegisterModule方法添加参数说明注释
- 为RegisterBuiltInModules方法添加容器和属性参数说明注释
- 为GetModules方法添加返回值说明注释
- 为InitializeAllAsync方法添加异步模式参数和返回值说明注释
- 为DestroyAllAsync方法添加返回值说明注释
- 在 ServiceModuleManager 中添加 _builtInModulesRegistered 标志位
- 实现重复注册检测逻辑,避免内置模块被多次注册
- 更新 RegisterBuiltInModules 方法的文档注释
- 在销毁所有模块时重置注册标志位
- 优化方法参数命名和描述信息
- 改进代码结构和注释内容
@GeWuYou GeWuYou merged commit 7046c2c into main Feb 23, 2026
6 checks passed
@GeWuYou GeWuYou deleted the feature/architecture-modularization-and-ecs- branch February 23, 2026 05:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant