PHPackages                             kepawni/serge - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. kepawni/serge

ActiveLibrary[API Development](/categories/api)

kepawni/serge
=============

Generates your CQRS/ES model classes from your GraphQL schema.

v0.7(4y ago)6361[4 issues](https://github.com/kepawni/serge/issues)[1 PRs](https://github.com/kepawni/serge/pulls)GPL-3.0-onlyPHP

Since Jan 23Pushed 1y agoCompare

[ Source](https://github.com/kepawni/serge)[ Packagist](https://packagist.org/packages/kepawni/serge)[ RSS](/packages/kepawni-serge/feed)WikiDiscussions master Synced yesterday

READMEChangelogDependencies (4)Versions (11)Used By (0)

serge — Swiftly Establish Rich GraphQL Endpoints
================================================

[](#serge--swiftly-establish-rich-graphql-endpoints)

Write your GraphQL schema and let serge generate your

- Command handlers
- Aggregates
- Event types
- Value objects

for CQRS and Event Sourcing.

Quick start
-----------

[](#quick-start)

Run `vendor/bin/serge-codegen` and notice a default configuration popping into your working directory. Change the settings and run the script again. This will give you a sample GraphQL schema to play with. Use it to define your model and from now on (with both files in place) calling `vendor/bin/serge-codegen` again will generate class files for you that make up your domain model.

**Important!** Use Git or another code versioning, because these classes will be overwritten every time the code generator is called. Alternatively you may change the target directory in the config file and carefully copy the classes into place on your own.

GraphQL schema
--------------

[](#graphql-schema)

To make this work, let's start off with the minimum requirements for our GraphQL schema. We will focus on the command side here, so we keep the query part to a minimum. In fact, it is recommended to have another Web-facing endpoint for the query side altogether.

```
schema {
    query: CqrsQuery
    mutation: CqrsAggregateMutators
}
type CqrsQuery {
    status: Boolean!
}

```

The *query* part must be present in a valid GraphQL schema, so we define *CqrsQuery* (you can name it however you want, by the way) as a regular *type*. To meet the requirement for types to have at least one field, we define *status*, so we have a simple means of testing if our endpoint is working at all. This can catch quite a few problems like missing dependencies, wrong config paths or just a server instance that didn't come up as planned.

Now for the *mutation* part, which is covered by another *type* we named *CqrsAggregateMutators* (again, you may change that name if you like). When we define this *type*, we add a field for every aggregate in our model:

### Declare aggregates

[](#declare-aggregates)

```
type CqrsAggregateMutators {
    Customer(id: ID!): Customer!
    Invoice(id: ID!): Invoice!
}

```

These fields need a parameter for passing the aggregate ID, so we use `id: ID!` as a hard-coded convention. The return types of these fields must match their names and are non-nullable, which is also a fixed convention. This way, we now have declared the aggregates available in our model and can now turn to the commands they are supposed to handle.

### Define commands per aggregate

[](#define-commands-per-aggregate)

So, we define another `type` for each of the aggregates and add our commands as fields.

```
type Invoice {
    chargeCustomer(customerId: ID!, invoiceNumber: String, invoiceDate: Date): ID!
    appendLineItem(item: LineItem!): Boolean!
    correctMistypedInvoiceDate(invoiceDate: Date!): Boolean!
    overrideDueDate(dueDate: Date!): Boolean!
    removeLineItemByPosition(position: Int!): Boolean!
}

```

The return type follows another simple convention:

- `ID!` for factory commands that bring a new aggregate into existence
- `Boolean!` for regular commands that mutate an existing aggregate

The field names should be lowercase, because these will be the public methods this aggregate provides.

### Define value objects

[](#define-value-objects)

You may notice, that these commands use both built-in types (e. g. `String`) and complex, yet undefined ones like `LineItem`. These are our value objects and this is how we define them as *input* types:

```
input LineItem {
    quantity: Float!
    price: Money!
    title: String
}
input Money {
    amount: Float!
    currency: String!
}

```

As you can see, they even may refer to each other forming a complex and flexible type system.

With these steps, we already have defined anything the code generator needs to know for building our model classes, but when value objects can be defined so easily, why not use this mechanism for the domain events as well?

### Define event types

[](#define-event-types)

```
interface InvoiceEvents {
    CustomerWasCharged(customerId: ID!, invoiceNumber: String, invoiceDate: Date): Boolean!
    LineItemWasAppended(item: LineItem!): Boolean!
    MistypedInvoiceDateWasCorrected(invoiceDate: Date!): Boolean!
    DueDateWasOverridden(dueDate: Date!): Boolean!
    LineItemWasRemoved(position: Int!): Boolean!
}

```

To distinguish between value objects and events, we use *interface*types here and another convention requires that we group events together based on the aggregates that emits them, which is indicated in the name (aggregate name + `Events`). This time, the field names start with an uppercase letter, because these will be turned into event classes and the parameters will be the event's properties. The `Boolean!` return type is just another convention here.

XML configuration
-----------------

[](#xml-configuration)

The GraphQL schema above defines the inner structure of our model, but in order to generate actual class files we need a bit more context on paths and namespaces and so on. That gap will be bridged by the file `serge.config.xml` (the name is fixed).

If you call the code generator the first time, when there is no such configuration file, it will generate one for you. See the quick start for how to get going from scratch.

###  Health Score

26

—

LowBetter than 43% of packages

Maintenance17

Infrequent updates — may be unmaintained

Popularity13

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity57

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~147 days

Recently: every ~239 days

Total

8

Last Release

1637d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/45966911?v=4)[Ailis Kepawni](/maintainers/kepawni)[@kepawni](https://github.com/kepawni)

---

Top Contributors

[![kepawni](https://avatars.githubusercontent.com/u/45966911?v=4)](https://github.com/kepawni "kepawni (117 commits)")

### Embed Badge

![Health badge](/badges/kepawni-serge/health.svg)

```
[![Health](https://phpackages.com/badges/kepawni-serge/health.svg)](https://phpackages.com/packages/kepawni-serge)
```

###  Alternatives

[nuwave/lighthouse

A framework for serving GraphQL from Laravel

3.5k10.7M93](/packages/nuwave-lighthouse)[thecodingmachine/graphqlite

Write your GraphQL queries in simple to write controllers (using webonyx/graphql-php).

5723.1M30](/packages/thecodingmachine-graphqlite)[wp-graphql/wp-graphql

GraphQL API for WordPress

3.8k391.7k19](/packages/wp-graphql-wp-graphql)[overblog/graphql-bundle

This bundle provides tools to build a GraphQL server in your Symfony App.

8027.9M28](/packages/overblog-graphql-bundle)[wheelpros/fitment-platform-api

Magento 2 (Open Source)

12.1k1.2k](/packages/wheelpros-fitment-platform-api)[aimeos/ai-admin-graphql

Aimeos Admin GraphQL API extension

944100.0k4](/packages/aimeos-ai-admin-graphql)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
