{"id":34912991,"url":"https://github.com/abhirajranjan/eventstore","last_synced_at":"2026-05-23T15:32:00.939Z","repository":{"id":65408400,"uuid":"588884577","full_name":"abhirajranjan/eventstore","owner":"abhirajranjan","description":"event store implementation using eventstore db for event sourcing ","archived":false,"fork":false,"pushed_at":"2023-01-21T14:37:39.000Z","size":16,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-27T21:55:35.062Z","etag":null,"topics":["event-sourcing","eventdriven","eventdrivenarchitecture","eventstore","eventstoredb","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/abhirajranjan.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2023-01-14T11:03:45.000Z","updated_at":"2025-03-06T15:40:05.000Z","dependencies_parsed_at":"2023-02-12T10:46:56.637Z","dependency_job_id":null,"html_url":"https://github.com/abhirajranjan/eventstore","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/abhirajranjan/eventstore","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhirajranjan%2Feventstore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhirajranjan%2Feventstore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhirajranjan%2Feventstore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhirajranjan%2Feventstore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/abhirajranjan","download_url":"https://codeload.github.com/abhirajranjan/eventstore/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/abhirajranjan%2Feventstore/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33402163,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-23T04:15:53.637Z","status":"ssl_error","status_checked_at":"2026-05-23T04:15:53.242Z","response_time":53,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["event-sourcing","eventdriven","eventdrivenarchitecture","eventstore","eventstoredb","golang"],"created_at":"2025-12-26T12:00:00.581Z","updated_at":"2026-05-23T15:32:00.933Z","avatar_url":"https://github.com/abhirajranjan.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# event-store\nevent-store is a golang implementation of event store used in event sourcing architecture to store and load [aggregates](https://www.eventstore.com/event-sourcing#Write-model) from [eventstoredb](https://www.eventstore.com/event-sourcing#Event-database-event-store)\n\n## Features\n* load aggregate directly from the aggregate store\n* embed aggregate struct and perform domain logic and call apply to apply aggregate\n\n## Basic Usage\naggregateStore handles all write tasks providing the aggregate logic\n\n```golang\nimport {\n    \"fmt\"\n\n    // golang client for eventstoredb\n    \"github.com/EventStore/EventStore-Client-Go/esdb\"\n\tes \"github.com/abhirajranjan/eventstore/pkg/eventstore\"\n}\n\nfunc main {\n    // eventstoredb config\n    setting, err := esdb.ParseConnectionString(\"esdb://localhost:2113?tls=false\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n    // eventstoredb client instance\n\tconn, err := esdb.NewClient(setting)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer conn.Close()\n\tfmt.Println(\"connection created\")\n\tstore := es.NewAggregateStore(logger, conn)\n}\n```\n\n## Events\n\nevents are entity that describe certain thing that has happened. Due to the fact that they happened in past makes them immutable.\n\nAbstract Event is defined as es.Event\n\n```golang\ntype event struct {\n\tEventID     string\n\tEventType   string\n\tData        []byte\n\tTimestamp   time.Time\n\tVersion     int64\n\tMetadata    []byte\n\tAggregateID string\n\tAggregateType\n}\n```\nuser generating new event have to set these feilds. \n\nData field is marshaled json byte object that has domain fields related to Aggregate Type\n\n### NewEventFromRecordedEvent\n\n```golang\nfunc NewEventFromRecordedEvent(revent *esdb.RecordedEvent) *event\n```\n\nNewEventFromRecordedEvent is use to load events already occured in past.\nInternally this function is used in stores to load event from eventstoredb.\n\n### EventFromEventData\n\n```golang\nfunc EventFromEventData(recordedEvent esdb.RecordedEvent) (event, error)\n```\n\nEventFromEventData is used to generate es.event from eventstoredb event type in deserialized form. used in stores to get events and call \"when\" method on aggregate.\n\n\n### NewEventFromEventData\n\n```golang\nfunc NewEventFromEventData(eventd esdb.EventData) event\n```\n\nNewEventFromEventData is use to generate event from eventData.\n\n### NewBaseEvent\n\n```golang\nNewBaseEvent(aggregate Aggregate, eventType string) event\n```\nused to generate new event for aggregate.\nit generates new EventID and set Event ID, EventType, EventVersion as aggregateID, aggregateType, AggregateVersion respectively.\nnew timestamp is also initalized as current time.Time object.\n\n## Aggregate\n\n```golang\ntype Aggregate struct {\n\tes.AggregateBase\n\tnewID int64\n    name string\n}\n```\n\ncreate a custom aggregate struct to embed AggregateBase with additional domain fields to get aggregate methods\n\n### when function\n\nembedding AggregateBase with custom fields to store the current state.\n\n\"When\" method needs to be implemented in new embedded struct to maintain current state and  apply domain logic event applied to it\n\nNote: \"When\" method should not be directly called in the code, instead call \"Apply\" method on the aggregate to apply an event which internally calls \"When\" in addition to maintaining all the uncommited events\n\n```golang\ntype Data struct {\n\tItem  string\n\tValue int64\n}\n\ntype Aggregate struct {\n\tes.AggregateBase\n    // embed Data having domain fields\n\tData Data\n}\n\n// When method takes event of type es.Event and return error\nfunc (a *Aggregate) When(event es.Event) error {\n\tswitch event.GetEventType() {\n\tcase \"setItem\":\n\t\tvar d Data\n\t\tif err := json.Unmarshal(event.GetData(), \u0026d); err != nil {\n\t\t\treturn fmt.Errorf(\"unmarshling error in setItem\")\n\t\t}\n\t\ta.Data.Item = d.Item\n\tcase \"setValue\":\n\t\tvar d Data\n\t\tif err := json.Unmarshal(event.GetData(), \u0026d); err != nil {\n\t\t\treturn fmt.Errorf(\"unmarshling error in setValue\")\n\t\t}\n\t\ta.Data.Value = d.Value\n\tdefault:\n\t\treturn fmt.Errorf(\"event type %v not defined\", event.GetEventType())\n\t}\n\tfmt.Printf(\"(item) %s %d\\n\", a.Data.Item, a.Data.Value)\n\treturn nil\n}\n```\n\n### create new Aggregate\n\ninitate new aggregate. aggregateRoot is an interface that provides \"When\" function for \"apply\" method.\n\n```golang\n// initiate aggregate\ninitalaggregate := \u0026Aggregate{}\n// set the AggregateRoot interface to itself so that it can call embedded \"When\" function\ninitalaggregate.AggregateRoot = initalaggregate\n// set inital version to -1 \n// version automatically increments by 1 so the event commited with version 0\ninitalaggregate.SetVersion(-1)\n// set aggregate type to event type matched in \"When\" function\ninitalaggregate.SetType(\"setItem\")\n```\n\n### load current state of aggregate\n\nAggregateStore \"load\" method is used to load current state of aggregate into aggregate object.\n\n```golang\n// create new aggregate\ninitialaggregate := \u0026Aggregate{}\ninitialaggregate.AggregateRoot = initialaggregate\ninitialaggregate.SetType(\"setItem\")\n\nstore := es.NewAggregateStore(logger, conn)\n\n// load events sequentially into initalaggregate to get current state\nstore.Load(context.TODO(), \u0026initialaggregate)\n```\n\n\n### apply method\n\n\"Apply\" event is used to apply the event to current aggregate.\nit internally calls aggregate's \"When\" method to maintain the current state and if no error was returned it appends event to uncommited events.\n\n```golang\n// create new event from base event\nevent := es.NewBaseEvent(initalaggregate, \"setItem\")\ndata, err := json.Marshal(Data{Item: \"gate\", Value: 1})\nif err != nil {\n\tpanic(err)\n}\n\nevent.SetData(data)\nfmt.Println(\"id: \", initalaggregate.GetID())\n\nif err := initalaggregate.Apply(\u0026event); err != nil {\n\tpanic(err)\n}\n```\n\n## AggregateStore\n\naggregate store gives functions used to to easily restore and check aggregate's current state in eventstoredb\n\n### Load function\n\n```golang\nfunc (as *aggregateStore) Load(ctx context.Context, aggregate Aggregate) error\n```\n\nget the current state of aggregate based on aggregateID from database by getting events sequentially and calling aggregate \"when\" method to perform domain logic\n\nfor easy debugging, applied events are stored in aggregate to easily provide tracking of events\n\n### Save function\n\n```golang\nfunc (as *aggregateStore) Save(ctx context.Context, aggregate Aggregate) error\n```\n\nSave function is used to Save all uncommited events in aggregate to database to persist changes\n\nSave function should be called after all domain checks and processing of event in current state of aggregate\n\n### Exists function\n\n```golang\nfunc (as *aggregateStore) Exists(ctx context.Context, streamID string) error\n```\n\nExists checks if the current aggregateID is present in eventstore or not.\n\nif given aggregateID does not exists, it returns esdb.ErrNoStreamFound\nif given aggregateID exists in eventstore, it returns nil\nif any other error occured, it returns wrapped error","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabhirajranjan%2Feventstore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fabhirajranjan%2Feventstore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fabhirajranjan%2Feventstore/lists"}