I think you could improve the layout of your code to be more idiomatic. Simply put there are too many files and too many packages.
Consider merging each package into a single pair of files (.go and test) and merging them all into a single package.
I think you'll find the number of types exported shrinks dramatically and the visible surface are of your package will become smaller.
Dave
--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/Ro4Ba-8MVOg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to a topic in the Google Groups "golang-nuts" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/golang-nuts/Ro4Ba-8MVOg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to golang-nuts...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
In the example create a package called "invite" then you can name the events/lists as
invite.Infoinvite.Projector
invite.Createdinvite.Acceptedinvite.DeclinedRegarding the different packages... I think you went too far with the flattening :)As a general tip, if you have 3 or more parts for your interface/struct name then you should structure them better.
I didn't yet intend to upload my approach, but... https://github.com/egonelbre/eventI'm not convinced that the aggregate approach as in most CQRS examples is good enough for Go.
With my approach when implementing some aggregate you would do it:package thingtype Aggregate struct {event.Aggregate}func (thing *Aggregate) Apply(e event.Event) {thing.Record(e)switch e := e.(type) {case Created: ...// and methodsfunc (thing *Aggregate) Append(value string){thing.Apply(Appended{value})}
The repository would look like:package thingtype Repository struct {event.Store}func (repo *Repository) Create() *Aggregate {ep := &Aggregate{}ep.Id = event.GenerateId()return ep}
func (repo *Repository) ById(id event.AggregateId) (thing *Aggregate, ok bool) {events, ok := repo.Store.List(id)if !ok {return nil, false}ep = &Aggregate{}ep.Id = idfor _, event := range events {ep.Version = event.Versionep.Apply(event.Data)}ep.Changes = nilreturn ep, true}In the main entry points...type ThingService struct {store event.Storerepo *thing.Repository}func (srv *ThingService) Apply(id event.AggregateId, text string) error {thing, ok := srv.repo.ById(id)if !ok { return fmt.Errorf("thing doesn't exist") }thing.Apply(text)return srv.store.SaveChanges(thing)}Anyways... I'm not comfortable that the Aggregate composes the current state and recording of changes, but I haven't found a way to resolve that nicely.Hopefully it gives some ideas how to improve things, but I'm not convinced that either one is the best solution for Go.
+ Egon
Hi Egon,Interesting points on the design. Read my comments below!
On Tuesday, November 18, 2014 11:32:32 AM UTC+1, egon wrote:In the example create a package called "invite" then you can name the events/lists as
invite.Infoinvite.Projector
invite.Createdinvite.Acceptedinvite.DeclinedRegarding the different packages... I think you went too far with the flattening :)As a general tip, if you have 3 or more parts for your interface/struct name then you should structure them better.Maybe I went too far, and it was mostly on purpose after feedback from Dave. The event store and read repository are definitely candidates for separate packaging. But as suggested above I will let that happen when the need for it comes.
I didn't yet intend to upload my approach, but... https://github.com/egonelbre/eventI'm not convinced that the aggregate approach as in most CQRS examples is good enough for Go.Sweet, another library! I'll definitely have a closer look at it. Always interesting to see different solutions.With my approach when implementing some aggregate you would do it:package thingtype Aggregate struct {event.Aggregate}func (thing *Aggregate) Apply(e event.Event) {thing.Record(e)switch e := e.(type) {case Created: ...// and methodsfunc (thing *Aggregate) Append(value string){thing.Apply(Appended{value})}The Aggregate in Event Horizon can also be implemented with a switch on the type, for example named AssertAggregate. As a matter of fact I used that approach in another app where the event handler is Nanomsg-based.
The repository would look like:package thingtype Repository struct {event.Store}func (repo *Repository) Create() *Aggregate {ep := &Aggregate{}ep.Id = event.GenerateId()return ep}I suggest generating the ID in the UI/client, recommended by Greg Young. This way the UI can track the item right from the start by observing the ID it used when creating it.
func (repo *Repository) ById(id event.AggregateId) (thing *Aggregate, ok bool) {events, ok := repo.Store.List(id)if !ok {return nil, false}ep = &Aggregate{}ep.Id = idfor _, event := range events {ep.Version = event.Versionep.Apply(event.Data)}ep.Changes = nilreturn ep, true}In the main entry points...type ThingService struct {store event.Storerepo *thing.Repository}func (srv *ThingService) Apply(id event.AggregateId, text string) error {thing, ok := srv.repo.ById(id)if !ok { return fmt.Errorf("thing doesn't exist") }thing.Apply(text)return srv.store.SaveChanges(thing)}Anyways... I'm not comfortable that the Aggregate composes the current state and recording of changes, but I haven't found a way to resolve that nicely.Hopefully it gives some ideas how to improve things, but I'm not convinced that either one is the best solution for Go.Thanks for the example, again very useful to see another implementation!With Event Horizon I'm trying to build something that is flexible enough to be configured in all sizes, from in memory testing to full scale distributed DBs in production with different DB-backings for the event store and the read model. That is why I have tried to use interfaces in many places, which also helps a lot in testing by being able to create mocks without a library! I will make the necessary changes needed to support all use cases along the way, as I'm currently using it in a large scale system implementation.
+ EgonI would also love to explore using Go channels in some way, I have a feeling they can be of some use!
On Tuesday, 18 November 2014 14:56:21 UTC+2, Max Persson wrote:Hi Egon,Interesting points on the design. Read my comments below!
On Tuesday, November 18, 2014 11:32:32 AM UTC+1, egon wrote:In the example create a package called "invite" then you can name the events/lists as
invite.Infoinvite.Projector
invite.Createdinvite.Acceptedinvite.DeclinedRegarding the different packages... I think you went too far with the flattening :)As a general tip, if you have 3 or more parts for your interface/struct name then you should structure them better.Maybe I went too far, and it was mostly on purpose after feedback from Dave. The event store and read repository are definitely candidates for separate packaging. But as suggested above I will let that happen when the need for it comes.The separate packing isn't actually for separation concerns... it's because the names will get shorter and clearer. (Similarly to the "invite" package).
I didn't yet intend to upload my approach, but... https://github.com/egonelbre/eventI'm not convinced that the aggregate approach as in most CQRS examples is good enough for Go.Sweet, another library! I'll definitely have a closer look at it. Always interesting to see different solutions.With my approach when implementing some aggregate you would do it:package thingtype Aggregate struct {event.Aggregate}func (thing *Aggregate) Apply(e event.Event) {thing.Record(e)switch e := e.(type) {case Created: ...// and methodsfunc (thing *Aggregate) Append(value string){thing.Apply(Appended{value})}The Aggregate in Event Horizon can also be implemented with a switch on the type, for example named AssertAggregate. As a matter of fact I used that approach in another app where the event handler is Nanomsg-based.I initially tried several ways with reflection and all of those had too much "magic" and I didn't like where those implementations were going. Eventually decided to simply use a switch.
I really enjoy the discussion! Here are my thoughts:
On Tuesday, November 18, 2014 2:22:13 PM UTC+1, egon wrote:
On Tuesday, 18 November 2014 14:56:21 UTC+2, Max Persson wrote:Hi Egon,Interesting points on the design. Read my comments below!
On Tuesday, November 18, 2014 11:32:32 AM UTC+1, egon wrote:In the example create a package called "invite" then you can name the events/lists as
invite.Infoinvite.Projector
invite.Createdinvite.Acceptedinvite.DeclinedRegarding the different packages... I think you went too far with the flattening :)As a general tip, if you have 3 or more parts for your interface/struct name then you should structure them better.Maybe I went too far, and it was mostly on purpose after feedback from Dave. The event store and read repository are definitely candidates for separate packaging. But as suggested above I will let that happen when the need for it comes.The separate packing isn't actually for separation concerns... it's because the names will get shorter and clearer. (Similarly to the "invite" package).I don't like using packages for the convenience of shorter names, autocompletion in my editor solves that.
I do like types to have a name that is useful in logging with %#v, ie CreateUser instead of Create.
Hi Egon,I have now implemented a new dispatcher and aggregate that uses delegation
--