Skip to content

feat(core): refactor commands to use handler functions (Magek v2 Phase 1)#733

Open
javiertoledo wants to merge 2 commits intomainfrom
declarative-syntax
Open

feat(core): refactor commands to use handler functions (Magek v2 Phase 1)#733
javiertoledo wants to merge 2 commits intomainfrom
declarative-syntax

Conversation

@javiertoledo
Copy link
Member

Summary

This PR implements the first phase of Magek v2's data-oriented refactoring, focusing on commands. The key architectural change is moving from class-based command handlers to direct handler functions, enabling a DSL-first approach where decorators become thin wrappers.

Changes

  • New DSL function: command() in packages/core/src/dsl/command.ts creates command metadata with handler functions
  • Updated CommandMetadata: Uses handler function instead of class reference, adds name property
  • Updated CommandDispatcher: Calls handler(input, register) directly instead of class.handle()
  • Reimplemented @Command decorator: Now a thin wrapper around the DSL command() function
  • Updated GraphQL infrastructure:
    • TargetTypeMetadata uses name instead of class
    • Added generateGraphQLTypeForMetadata() for metadata-based type generation
    • Added transform functions for CommandMetadata → TargetTypesMap

Breaking Changes

  • CommandMetadata.class replaced with CommandMetadata.handler
  • CommandMetadata now requires name property

Architecture (Before → After)

// Before (v1)
Decorators → Create metadata with class references → Runtime instantiates classes

// After (v2)
DSL Functions → Create pure data metadata → Runtime uses handlers directly
    ↑
Decorators (thin wrappers that call DSL functions)

Test plan

  • All 327 tests in @magek/core pass
  • Full monorepo build passes (rush build)
  • Lint checks pass (rush lint:check)
  • Full monorepo test suite passes (rush test)

Next Steps (Future PRs)

Following the incremental migration plan:

  1. ✅ Commands (this PR)
  2. Events - Add event() DSL, update EventMetadata
  3. Entities + Reducers
  4. Event Handlers
  5. Read Models + Projections
  6. Queries

🤖 Generated with Claude Code

…sses

This is the first phase of Magek v2's data-oriented refactoring. Commands now
use handler functions directly instead of class references, making the framework
more composable and enabling a DSL-first approach.

Changes:
- Add `command()` DSL function in packages/core/src/dsl/command.ts
- Update CommandMetadata to use `handler` function instead of `class`
- Add `name` property to CommandMetadata
- Update CommandDispatcher to call handler directly
- Reimplement @command decorator as thin wrapper around DSL
- Update GraphQL infrastructure to work with new metadata format
- Add `generateGraphQLTypeForMetadata()` for metadata-based type generation

Breaking changes:
- CommandMetadata.class replaced with CommandMetadata.handler
- CommandMetadata now requires `name` property

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b144b777e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +29 to +32
const handler = async (input: unknown, register: Register): Promise<unknown> => {
// Create an instance from the input (mirroring old createInstance behavior)
const instance = Object.assign(Object.create(commandClass.prototype), input)
return commandClass.handle(instance, register)

Choose a reason for hiding this comment

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

P2 Badge Preserve constructor side effects when wrapping @command classes

The decorator now builds the handler by Object.assign(Object.create(commandClass.prototype), input) and skips calling the class constructor, whereas the old path used createInstance() which does new instanceClass() and therefore runs constructor logic. Any existing command class that sets defaults, validates, or derives fields in its constructor will now receive an instance without those side effects, which can change command handling behavior at runtime. If constructors are intentionally supported today, consider instantiating with new commandClass() (or reusing createInstance) to keep parity with the previous behavior.

Useful? React with 👍 / 👎.

- Commit the pnpm-lock.yaml which is required for CI/CD
- Update CLAUDE.md with reminders to always run `rush update` and commit
  the lockfile before pushing changes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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