{"id":28680714,"url":"https://github.com/elder-oss/sourcerer","last_synced_at":"2026-01-11T16:57:19.870Z","repository":{"id":12937993,"uuid":"73185566","full_name":"elder-oss/sourcerer","owner":"elder-oss","description":"An opinionated, functional, and storage agnostic framework for implementing a CQRS architecture in Java 8 using event sourcing.","archived":false,"fork":false,"pushed_at":"2023-10-23T11:33:42.000Z","size":789,"stargazers_count":76,"open_issues_count":6,"forks_count":5,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-04-17T10:55:28.070Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elder-oss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2016-11-08T12:51:25.000Z","updated_at":"2024-04-04T10:53:30.000Z","dependencies_parsed_at":"2022-07-31T10:38:08.851Z","dependency_job_id":"cc231b13-592b-4956-a120-1bee2259c6e9","html_url":"https://github.com/elder-oss/sourcerer","commit_stats":null,"previous_names":[],"tags_count":78,"template":false,"template_full_name":null,"purl":"pkg:github/elder-oss/sourcerer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elder-oss%2Fsourcerer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elder-oss%2Fsourcerer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elder-oss%2Fsourcerer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elder-oss%2Fsourcerer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elder-oss","download_url":"https://codeload.github.com/elder-oss/sourcerer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elder-oss%2Fsourcerer/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":259747197,"owners_count":22905308,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-06-14T02:01:11.546Z","updated_at":"2026-01-11T16:57:19.853Z","avatar_url":"https://github.com/elder-oss.png","language":"Java","funding_links":[],"categories":["开发框架"],"sub_categories":[],"readme":"### About Elder\n\nElder is a UK based introductory care agency that uses technology to rethink in-home care for\nelderly, for more information, see [www.elder.org](http://www.elder.org/?utm_source=github).\nFor questions about Elder open source software or technology at Elder in general, please contact\ntech at elder dot org.\n\nSourcerer\n=========\n\n## Overview\n\nSourcerer is an **opinionated**, **functional**, and **storage agnostic** framework for\nimplementing a CQRS architecture in Java 8 using event sourcing. \n\n### Sourcerer is opinionated\n\nSourcerer is a framework that supports a set of core concepts used in CQRS/ES architectures and\nhas its own opinion on what these building blocks are and how they are implemented. While it can\nbe used as an abstraction layer for any event based system, it is designed to enable the user to\nexpress business logic in terms of specific patterns such as commands, operations, and subscriptions\n(see below).\n\n### Sourcerer is functional\n\nSourcerer embraces functional style programming - preferring to use immutable dumb objects over\nobject oriented stateful aggregates. State is expressed as a function over events, and events are\ncreated explicitly, rather than as an implementation detail hidden inside an aggregate object.\n\n### Sourcerer is storage agnostic\n\nThe storage backend used for the reference implementation and used in production at Elder is \n[EventStore](http://www.geteventstore.com), but alternative storage backends can be used by\nimplementing a [single interface](https://github.com/elder-oss/sourcerer/blob/master/sourcerer-core/src/main/java/org/elder/sourcerer/EventRepository.java).\n\n## Patterns\n\nWhile sourcerer uses a number of lower level abstractions to re-use implementation logic and allow\nfor extensibility, normal business logic will usually only need to deal with the patterns outlined\nbelow.\n\n### Commands\n\nA sourcerer command is a function that, when executed, creates or updates an event sourced\naggregate. Commands are created from a\n[Command Factory](https://github.com/elder-oss/sourcerer/blob/master/sourcerer-core/src/main/java/org/elder/sourcerer/CommandFactory.java), implicitly bound to an underlying event repository and projection. Commands are created from an operation (see below), as well as additional metadata such as whether it can create new aggregates, whether it requires changes to applied atomically (with no concurrent changes) etc.\n\n#### Aggregate Projections\n\nA command factory is bound to an\n[Aggregate Projection](https://github.com/elder-oss/sourcerer/blob/master/sourcerer-core/src/main/java/org/elder/sourcerer/AggregateProjection.java)\nthat contains the logic required to re-build aggregate state from a sequence of events.\nProjections can also be used directly in operations (see below) to preview state changes before\nthey have been committed to the underlying event repository.\n\n#### Operations\n\nAn Operation, in sourcerer, is created from a function (not necessarily a pure function) that\nprovides the business logic for a command. Operations accept an optional current state of an\naggregate, an optional parameter payload, and returns a seqence of events describing the changes\nto the aggregate. Operations may use external servics and data sources to generate new events, but\ndo not directly read or persist events related to the aggregate being updated - and as such can be\nexecuted speculatively for \"dry run\" scenarios and unit tested without involving event persistence.\nOperations are invoked by commands (see above) that deal with id, recreating state from events,\npersisting events and providing optimistic concurrency if requested.\n\nOperations are usually created from a method reference using the\n[Operations](https://github.com/elder-oss/sourcerer/blob/master/sourcerer-core/src/main/java/org/elder/sourcerer/Operations.java)\nclass, rather than by implementing the\n[Operation](https://github.com/elder-oss/sourcerer/blob/master/sourcerer-core/src/main/java/org/elder/sourcerer/Operation.java)\ninterface directly. The framework provides a large number of Operation Handler functional interfaces\nmatching common method type signatures and allows for automatic conversion from method references\nin Java 8.\n\n### Subscriptions\n\nSourcerer subscriptions are used to respond to updates to aggregates (via emitted events). In the\nrecommended sourcerer architecture, subscriptions is the only way in which materialized query models\nare being created and kept up-to-date. Subscriptions can also be used to trigger side effects on\nevents, such as sending emails when certain actions are met.\n\nSourcerer subscriptions are responsible for keeping track of their own position in an event stream\n- being either a stream of events with a particular stream id, or a stream of all events of a given\nbase type (e.g. all events related to a User). They must cope gracefully with being restarted at\nany point in time, due to network disconnects, process restarts etc, so it is recommended that the\nimplementation is effectively idempotent - allowing events to be replayed without extra unwanted\nside effects.\n\nSubscriptions are created from an \n[EventSubscriptionFactory](https://github.com/elder-oss/sourcerer/blob/master/sourcerer-core/src/main/java/org/elder/sourcerer/EventSubscriptionFactory.java),\nimplicitly bound to an event repository and event type, by providing an implementation of\n[EventSubscriptionHandler](https://github.com/elder-oss/sourcerer/blob/master/sourcerer-core/src/main/java/org/elder/sourcerer/EventSubscriptionHandler.java).\nThe default subscription factory implementation will create subscriptions that handle automatic\nrestarts on errors with exponential back-off delays, and efficient batching that dynamically\nadjusts the number of events that the subscription handler is asked to process at a time depending\non the circumstances (live vs replay of old events, slow vs fast subscription handler).\n\n## Sample Projects\n\nFor sample projects using Sourcerer to implement CQRS with event sourcing, see [sourcerer-samples](https://github.com/elder-oss/sourcerer-samples).\n\n## Implementation specifics\n\n### Serialization\n\nThe core sourcerer modules are agnostic to the format used to serialize and persist events, leaving\nthis concern to the storage specific event repository implementation. The reference implementation\nusing EventStore uses Jackson to serialize and deserialize Java objects. When using polymorphic\nevent types (where individual event types on a given stream are represented in a Java class\nhierarchy), special concern must be taken to annotate the types appropriately, see\n[Jackson Polymorphic Deserialization](http://wiki.fasterxml.com/JacksonPolymorphicDeserialization).\n\n### Use of Kotlin\n\nSourcerer does not use or have dependencies on Kotlin, however, the sample projects and production\ncode that uses the framework does. Using Kotlin\n[sealed classes](https://kotlinlang.org/docs/reference/classes.html#sealed-classes) to represent\nevents in combination with [when expressions](https://kotlinlang.org/docs/reference/control-flow.html#when-expression)\nprovides type safe runtime inspection of event types that would only be possible in plain Java\nthrough runtime type inspections with unsafe downcasts, or using variations of the\n[visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern).\n\n### Spring support\n\nSourcerer provides optional support for use with Spring through standalone modules.\n\n## Supported storage backends\n\nThe reference implementation of sourecerer event repository uses EventStore as the backing data\nstore, and the design of event repository abstractions have admittedly been influenced by the\nprimitives offered by this product. In fact, sourcerer comes with two implementations - one based\nin the official [EventStore.JVM client](https://github.com/EventStore/EventStore.JVM),\nand one based on the [Java 8 native client](https://github.com/msemys/esjc). The preference at Elder\nis to use the esjc client, as this avoids dependencies on the Akka runtime, and has overall caused\nless unexpected surprises. The EventStore.JVM storage backend is maintained for compatibility only\nbut not actively in use by Elder.\n\nOther backends such as in memory event stores or ones backed by traditional databases plus a\nmessaging layer can be used by implementing\n[EventRepository](https://github.com/elder-oss/sourcerer/blob/master/sourcerer-core/src/main/java/org/elder/sourcerer/EventRepository.java).\n\n## Building project locally\n\nWhen performing a build, all tests are running, including the integration tests.\n\nThe integration tests expects a test eventstore instance to be running (see\n[circle.yml](circle.yml)).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felder-oss%2Fsourcerer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felder-oss%2Fsourcerer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felder-oss%2Fsourcerer/lists"}