{"id":13577142,"url":"https://github.com/yehohanan7/flux","last_synced_at":"2025-04-13T09:43:04.362Z","repository":{"id":144202256,"uuid":"76385040","full_name":"yehohanan7/flux","owner":"yehohanan7","description":"A simple CQRS Framework for go","archived":false,"fork":false,"pushed_at":"2022-12-27T18:16:49.000Z","size":518,"stargazers_count":216,"open_issues_count":1,"forks_count":12,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-03-27T01:11:17.568Z","etag":null,"topics":["cqrs","domain-driven-design","event-sourcing","go","golang","microservices"],"latest_commit_sha":null,"homepage":"","language":"Go","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/yehohanan7.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,"roadmap":null,"authors":null}},"created_at":"2016-12-13T18:04:39.000Z","updated_at":"2024-02-10T21:11:44.000Z","dependencies_parsed_at":null,"dependency_job_id":"70d71147-085e-49b6-8e30-ae32bbdde6e0","html_url":"https://github.com/yehohanan7/flux","commit_stats":null,"previous_names":["yehohanan7/cqrs"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yehohanan7%2Fflux","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yehohanan7%2Fflux/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yehohanan7%2Fflux/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/yehohanan7%2Fflux/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/yehohanan7","download_url":"https://codeload.github.com/yehohanan7/flux/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248693812,"owners_count":21146863,"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":["cqrs","domain-driven-design","event-sourcing","go","golang","microservices"],"created_at":"2024-08-01T15:01:18.464Z","updated_at":"2025-04-13T09:43:04.331Z","avatar_url":"https://github.com/yehohanan7.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# Flux [![Build Status](https://travis-ci.org/yehohanan7/flux.svg)](https://travis-ci.org/yehohanan7/flux?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/yehohanan7/flux)](https://goreportcard.com/report/github.com/yehohanan7/flux)\n![logo](http://www.logogala.com/images/uploads/gallery/octopus.png)\n\n\n# Introduction\n*\"There is nothing called state. There are events and the story we tell about what it means.\"*\n\nFlux allows you to quickly build an application in CQRS way without the hassle of a messaging system like RabbitMQ or Kafka inbetween your command and read model services.\n\nIt's a good practice to have one command service per Aggregate (as per DDD terminology) and various read model/view services. the command service stores the events that are emited by each command and expose the same as a json feed for the consumers (read model services) to consume in regular intervals allowing you to easily decouple commands and \"read model\" services.\n\n## Components\n![architecture](https://raw.githubusercontent.com/yehohanan7/flux/master/static/architecture.png)\n\n### Aggregate\n[Aggregates](https://martinfowler.com/bliki/DDD_Aggregate.html) accepts commands and generates events.\n\nThis is how you can define an aggregate in Flux:\n\n```go\nimport(\n  \"github.com/yehohanan7/flux\"\n  \"github.com/yehohanan7/flux/cqrs\"\n)\n\n//Account is an aggregate\ntype Account struct {\n\tcqrs.Aggregate\n\tId      string\n\tBalance int\n}\n\n//Initialize the aggregate\nacc := new(Account)\nacc.Aggregate = cqrs.NewAggregate(\"account-id\", acc, flux.NewBoltStore(\"path/to/database\"))\n```\n\nThe last argument is an EventStore, which provides an implementation to store and retrieve events - there are 2 implementations at the moment an inmemory one and a boltdb implementation\n```go\nstore := flux.NewMemoryStore()\n```\n\nOnce you have the aggregate initialized, you can execute commands on it which will in turn emit events, make sure to update the state of the aggregate through a handler method (prefixed with the name *Handle*) on the aggregate\n```go\n//My event\ntype AccountCredited struct {\n\tAmount int\n}\n\n//Command\nfunc (acc *Account) Credit(amount int) {\n\tacc.Update(AccountCredited{amount})\n}\n\n//Event handler to allow you to update the state of the aggregate as a result of a command\nfunc (acc *Account) HandleAccountCredited(event AccountCredited) {\n\tacc.Balance = acc.Balance + event.Amount\n}\n\n\n//Execute command\nacc.Credit(100)\nacc.Credit(150)\nif err := acc.Save(); err == cqrs.Conflict {\n  //this error is due to concurrent modification of the aggregate, you should retry the request\n}\n\n```\n\n\n### FeedHandler\nFeed handler allows you to publish the events as a json feed for the outside world.\n\n```go\nrouter.HandleFunc(\"/events\", flux.FeedHandler(store))\nrouter.HandleFunc(\"/events/{id}\", flux.FeedHandler(store))\n```\n\nSame feed exposed by the endpoint /events is as below\n\n```json\n{\n  \"description\": \"event feed\",\n  \"events\": [\n    {\n      \"event_id\": \"47d074c3-ba83-40e2-8720-804b73a202b9\",\n      \"url\": \"http://localhost:3000/events/47d074c3-ba83-40e2-8720-804b73a202b9\",\n      \"aggregate_name\": \"*account.Account\",\n      \"aggregate_version\": 0,\n      \"event_type\": \"account.AccountCreated\",\n      \"created\": \"Fri Apr  7 15:19:18 2017\"\n    },\n    {\n      \"event_id\": \"174a40b6-104a-4be5-a352-4e61b524d3dc\",\n      \"url\": \"http://localhost:3000/events/174a40b6-104a-4be5-a352-4e61b524d3dc\",\n      \"aggregate_name\": \"*account.Account\",\n      \"aggregate_version\": 1,\n      \"event_type\": \"account.AccountCredited\",\n      \"created\": \"Fri Apr  7 15:19:27 2017\"\n    }\n  ]\n}\n```\n\n### EventConsumer\nEvent consumer allows you to consumer the events emitted by the aggreate in another service. you can start the event consumer like shown below, in the below example the consumer polls the aggregate service every 5 seconds to check for new events.\n\n```go\n//stores the offset to know where to consumer from after a restart\noffsetStore := flux.NewMemoryOffsetStore()\nconsumer := flux.NewEventConsumer(\"http://entityservicehost:port/events\", 5 * time.Second, []interface{}{AccountCredited{}}, offsetStore)\neventCh := make(chan interface{})\n\n//Start consuming\ngo consumer.Start(eventCh)\n\n//Fetch the events and build your read models\nfor {\n  event := \u003c-eventCh\n  switch event.(type) {\n  case AccountCredited:\n    fmt.Println(event.(AccountCredited))\n  }\n}\n```\n\nYou could pause,resume \u0026 stop the consumer\n```go\nconsumer.Pause()\nconsumer.Resume()\nconsumer.Stop()\n```\n\n### ReadModel\nRead model is nothing but the result of how you interpret the events provided by the consumer shown above.\n\n## Sample application\nThere is a simple example application [here](https://github.com/yehohanan7/flux/tree/master/examples/bank) if you would like to refer\n\n## MongoDB\nmongodb as your event \u0026 offset store using [mgo](https://github.com/go-mgo/mgo)\n\n```go\nimport (\n  \"gopkg.in/mgo.v2\"\n  \"github.com/yehohanan7/flux\"\n  \"github.com/yehohanan7/flux/mongodb\"\n)\n\n// Create mongodb session\nsession, err := mgo.Dial(\"localhost\")\n\n// ...\n\n// Create event store\neventStore := flux.NewMongoStore(\u0026mongodb.MongoEventStoreOptions{\n  Session:               session,\n  Database:              \"example\", // optional\n  EventCollection:       \"event\",\n  AggregateCollection:   \"aggregate\",\n  TransactionCollection: \"transaction\",\n})\n\n// Create offset store\noffsetStore := flux.NewMongoOffsetStore(\u0026mongodb.MongoOffsetStoreOptions{\n  Session:               session,\n  Database:              \"example\", // optional\n  Collection:            \"offset\",\n  StoreId:               \"a_unique_id_for_consumer\",\n})\n```\n\n## Roadmap\n- [ ] Optimize consumers by using websockets/server push\n- [ ] Support postgres\n- [ ] publish metrics to graphite\n- [ ] Support option to emit events to external systems if required.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyehohanan7%2Fflux","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyehohanan7%2Fflux","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyehohanan7%2Fflux/lists"}