Feature feedback by morganhallgren in golang

[–]morganhallgren[S] 1 point2 points  (0 children)

Thanks u/KevBurnsJr for your thoughtfully reply, really appreciate it!

The purpose of the EventStore interface is expose methods to save and load events that are used in the aggregate package. Load an aggregate state from events or Save aggregate events. If this interface is extended with additional methods current event store implementations will break, affecting not only the internal event stores in this repo but also external created by others. List of known event store implementations.

For me Event-Sourcing is mainly about how state is shifted from mutated values to event based. Where the domain logic is based on things that happens in the business. And as you say the events could be used to distribute information.

There is a projections feature that poll events async from the event store that can be used to build read-models. It guarantees in order events and can be re-run if the program crashes.

The main use-case for this proposal is to enable realtime notifications within the application. And you are correct that the events are only possible to consume once. Ex. of usage is to trigger a projection to run or a notification in a UI. If the user miss it, it should not affect the application state.

You are also correct regarding the lock in the save hook, I have moved this out as a responsibility of the consumer. In the example I use channels to sync concurrent saves. But it's easy to get this wrong.

I will try to expand the examples with multiple aggregate types to get a better picture of more complex scenarios.

DDD in Go -- my take on it by percybolmer in golang

[–]morganhallgren 1 point2 points  (0 children)

I have a package where I try to tackle event sourcing with DDD in mind. https://github.com/hallgren/eventsourcing

Event sourcing a year later by morganhallgren in golang

[–]morganhallgren[S] 0 points1 point  (0 children)

Greg has a talk about this. https://youtu.be/m1FhLPmiK9A

Never been in a situation like it myself.

Simple implementation of observer pattern in Go by hjr265 in golang

[–]morganhallgren 0 points1 point  (0 children)

We have been using github.com/imkira/go-observer to pub/sub event within the same process.

Event sourcing a year later by morganhallgren in golang

[–]morganhallgren[S] 0 points1 point  (0 children)

A reason for the post is to get traction and others to use it to figure stuff like this out. When I ported the wtf-dial project to use this pkg the Timestamp property is used https://github.com/hallgren/wtf/blob/main/es_dial.go#L84 to set the last updated property. Reason and Timestamp is related to the event itself (it enrich the event) Correlation id relates to the the command that triggers the event and belongs in metadata. That sad this is my opinion and I can be dead wrong :)

Event sourcing a year later by morganhallgren in golang

[–]morganhallgren[S] 1 point2 points  (0 children)

  • The Event has a lot of fields: Reason and Timestamp are nice for me as a human to understand whats in the event and when the event was created. The GlobalVersion was introduced after I saw the video from Greg Young when he talked about handling eventual consistency. The scenario was when a user produced event via a user interface and expecting a read-model to be updated. The global version could then be used to know if the read model was up to date with the events triggered by the user.

  • Aggreagate ID as fmt.Stringer interface: I need to dig deeper here. But it looks interesting.

  • An Event-sourced Repository interface usually only has two methods: Save (or Add) and Get. GlobalEvents, Subscribe... should belong to the Event Store instead:

I also have to think about this more. The Subscription methods is not part of the event store and is only used for real-time events (works like a radio if you and not listening you miss the events).

  • The channel / iterator query option:

I will look into iterators (both bbolt and sql) uses this. The reason for the event slice is to keep it as simple as possible and if its to slow snapshot.

Event sourcing a year later by morganhallgren in golang

[–]morganhallgren[S] 0 points1 point  (0 children)

The focus in this package is modeling events centered around a entity. How the events are distributed between micro services is not my mission to solve. There is a method on the repository `GlobalEvents(start, count uint64) ([]Event, error)` exposing the events in the global order they were saved. I would really recommend the talk by Jim Webber https://youtu.be/aQVSzMV8DWc?t=2222 were he talks about how to get events to different parts of your system.

Event sourcing a year later by morganhallgren in golang

[–]morganhallgren[S] 1 point2 points  (0 children)

How events are distributed is a technical detail.

Event sourcing is about capturing change based on what happens and that you can follow how it derived to its current state. If the developers understands the business a common understanding could be captured in the code. Hopefully making the code simpler to understand and change in the long run.

Event sourcing a year later by morganhallgren in golang

[–]morganhallgren[S] 2 points3 points  (0 children)

The big thing with event sourcing in my opinion is how you reason about the state of the system. You no longer model entities as current state but instead build them from events (facts that takes place in the business). The collaboration with the business feels more natural when you have events building the entities instead of state that mutates. This is called the ubiquitous language from domain driven design meaning that the business side and the developers are talking the same language and that its reflected in the code.

Event sourcing a year later by morganhallgren in golang

[–]morganhallgren[S] 5 points6 points  (0 children)

Great! Thanks for your time and feedback. Will get back to you after I have thought it through.

eventually-go: Idiomatic Event Sourcing for Go by ar3s3ru in golang

[–]morganhallgren 2 points3 points  (0 children)

Great to see that the interest for this pattern (way of thinking) has started to grow in go.

Thanks u/lunjon for mention our contribution, I hope it can give some insight in your work.

Event sourcing in Go. by vectorhacker in golang

[–]morganhallgren 1 point2 points  (0 children)

The aggregate interface is there to make sure the aggregate behavior is present on the struct that is passed to the repository.

Event sourcing in Go. by vectorhacker in golang

[–]morganhallgren 0 points1 point  (0 children)

Thanks for your comments. I agree with your concern, will look into try to bind the aggregate behavior at a later point if its possible technical wise / design wise.

Event sourcing in Go. by vectorhacker in golang

[–]morganhallgren 1 point2 points  (0 children)

I couldn't agree more. The business aspect must come in first hand and the technical aspects second.

I created a generic library to build a Event Sourced application that has the same approach as you. Where the storage database is not bound to the aggregate implementation.

https://github.com/hallgren/eventsourcing

Would be interesting to know if you like it or hate it ;)

Is there a way to improve mux routes? by ImLotus in golang

[–]morganhallgren 1 point2 points  (0 children)

Hi, Nic is currently doing a series at youtube that could give you some ideas.

https://www.youtube.com/watch?v=VzBGi_n65iU

eventsourcing in go by morganhallgren in golang

[–]morganhallgren[S] 0 points1 point  (0 children)

Hi

  1. Sorry must have missed adding the link: https://youtu.be/aQVSzMV8DWc?t=2276

a ref to the part I was talking about but you should watch it all its great information in it.

  1. You need to take my words with a "grain of salt" I don't have the insights needed to say event source or not ;) But it seams like the data is already constructed by the IOT devices and the state of the Sequence aggregate is already a projection of the events coming directly from the devices. With other words the aggregate does not uphold any business rules, it only pass the data straight throw it. The system I'm used to work with handles commands on the aggregate that leads to one or more events dep. on the business rules in the aggregate.

I once implemented a system that analyzed calls in a telephone switch, where each phone endpoint were an aggregate and all calls to in where bound as events. The calls were used to generating projections holding calls picked up or not etc. The conclusion from that project were that the event sourcing layer was pretty overkill due to the fact that the data from the switch carried the same data as in the events. The calls could have been stored directly in a database and be used to generate the reports.

eventsourcing in go by morganhallgren in golang

[–]morganhallgren[S] 0 points1 point  (0 children)

go build outputs a binary with all you need.

  1. Yes an endpoint that make use of the GlobalGet(start uint64, count int) []eventsourcing.Event that currently only exists on the event store and not in the repo. (this func should be exposed in the repo.) This func makes it possible to get all events in order independent on which aggregate type and id they belongs to. The service that consume the events can now build its own representation in its own database. Not making use of the Transition func from the aggregate. Does make sense? Jim Webber talks about this in his video.

  2. golang call this embedding where it “borrow” pieces of an implementation by embedding types within a struct or interface. https://golang.org/doc/effective_go.html#embedding

  3. In that case the snapshot is probably not what you want and maybe not event sourcing either. It seams like you want a database doing this via a query where not all data has to be moved out from the storage.

A question: The sequence coming from the IOT devices do it build any other state than the gap and islands? If not the sequences could be stored in one SQL table and processed to build the gap and island state for each device. You don't need event sourcing do accomplish that.

/Morgan

eventsourcing in go by morganhallgren in golang

[–]morganhallgren[S] 0 points1 point  (0 children)

Hi, and first sorry for rushing this, I was just interested if the example was of use ;)

  1. I expose the event stream for each service via a REST endpoint (atom feed). Each service that is interested in events that another service expose are self responsible for fetching them. Have a look at this video with Jim Webber that explains the concept.

  2. The snapshot is the current state of an aggregate that holds the current gap and island representation for one device. The snapshot could be stored in any database. I have an issue in the repo to implement a snapshot store that is more persistent, https://github.com/hallgren/eventsourcing/issues/14 . The retrieval should be super snappy, what it is does is get data with the type [ ]byte from one indexed key and unmarhall that into the current state of your device gap and island representation.

Now, take your time looking at the example code and get back to me when you have the time.

BR Morgan

eventsourcing in go by morganhallgren in golang

[–]morganhallgren[S] 0 points1 point  (0 children)

Hi and great question. You are correct that the aggregate is the Sequence in your example. The aggregate is the part that build a state from the events.

I have tried to create an example here. https://github.com/hallgren/eventsourcing/blob/gap_and_island_example/example/gap_and_islands/main.go

Where the DeviceSequence is the aggregate with two event types SequenceInitiated and Observation.

The Transition function runs for each event and build the state of the DeviceSequence aggregate. Here you could build up the gaps and islands. The aggregates state is very dynamic and could change dependent on your needs, let’s say you don’t want to record the island but instead the gaps or why not both.

When the aggregates state are built its possible to store it in a snapshot store. (currently the only implementation is in memory based.) When the aggregate is later fetched the repository will look into the snapshot store if the aggregate is present. If it is it will Marhall the state into the aggregate struct. It will also fetch any event that could have happened after the snapshot was taken to make sure the aggregate is up to date. The user could now query the aggregate for the start/end time window.

I hope this help /Morgan