Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add projection example for multiple aggregates #16

Merged
merged 22 commits into from
Nov 6, 2015

Conversation

svalaskevicius
Copy link
Owner

No description provided.

@svalaskevicius
Copy link
Owner Author

@Fristi what do you think? can you spot anything that could be improved easily?

@svalaskevicius svalaskevicius self-assigned this Oct 31, 2015
libraryDependencies += "com.chuusai" %% "shapeless" % "2.2.5"

resolvers += Resolver.sonatypeRepo("releases")

addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.6.3")

wartremoverErrors ++= Warts.allBut(Wart.Any, Wart.Nothing, Wart.Throw, Wart.IsInstanceOf, Wart.AsInstanceOf, Wart.NoNeedForMonad)
// wartremoverErrors ++= Warts.allBut(Wart.Any, Wart.Nothing, Wart.Throw, Wart.IsInstanceOf, Wart.AsInstanceOf, Wart.NoNeedForMonad)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: this is removed as there are lots of errors when using upickle. Options so far:

  • investigate how to ignore specifically upickle errors
  • swap upickle with an alternative lib
  • disable wart remover :(

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Decouple encoding/decoding of events? So we let our users pick their own serialization lib

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that should be already possible :)

I wonder if the wart remover was a good idea to have.. It works very well for the code in this repo but also limits the libraries we can use and a couldn't find a good way to disable it for implicit lookups from ext libs...

import InMemoryDb._

import scala.collection.immutable.TreeMap
import DbAdapters.InMemoryDb._
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO, referencing the actual db adapter is still bad here - extract consumeEvents?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

solved

@svalaskevicius
Copy link
Owner Author

Hi @Fristi, any feedback on this version?

currently the projection can be replayed from the start (events ordered by their creation) regardless of where in the process was it added.

there are still some items to refactor, esp direct usage of the inMemoryDb in projections

}
}
def emptyCounterProjection = Projection.empty[Data](TreeMap.empty, List(
(Counter.tag, createEventDataConsumer( (d: Data, t: Tag, id: AggregateId, v: Int, e: Counter.Event) => {
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: create intermediate type here

extract serialisation to a level above DB adapter operations and leave DB only for storing / retrieving strings?

final case class ReadAggregateExistence[E](id: AggregateId) extends EventDatabaseOp[E, Error Xor Boolean]
final case class ReadAggregate[E](id: AggregateId, fromVersion: Int) extends EventDatabaseOp[E, Error Xor List[VersionedEvents[E]]]
final case class AppendAggregateEvents[E](id: AggregateId, events: VersionedEvents[E]) extends EventDatabaseOp[E, Error Xor Unit]
final case class ReadAggregateExistence[E](tag: Tag, id: AggregateId) extends EventDatabaseOp[E, Error Xor Boolean]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In akka-persistence events get a persistenceId (which is a aggregateId) and zero or more tags. In these cases you have to supply both. It would be nice to read events by a specific tag or a aggregateId. When writing you should write them along with one aggregatedId, but zero or more tags. Checking if a aggregate exists should only be done with a aggregateId I think?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The semantics of the tags in this PR are a bit different from akka - its more like aggregate class - eg all counters, or all doors. Checking if exists also needs it in this case - "is there a counter (by tag) identified by this id".

Projections tell what classes / tags of aggregates they are interested in and receive all aggregates within them

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, makes abit sense. However, how would you read all events of a counter for example without knowing all the aggregateIds? case class ReadAggregate[E](tag: Tag, id: AggregateId, fromVersion: Int) requires you to specify a AggregateId

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for this, there is currently a separate Database method as defined by the typeclass - https://github.com/svalaskevicius/eventflow/blob/multiEventsProjection/src/main/scala/Cqrs/Database.scala#L12

its not available in the aggregate actions as no aggregate should ever need that.. I think :)

@Fristi
Copy link
Contributor

Fristi commented Nov 1, 2015

Added some comments here and there.

Overall I need to take a closer look what all constructs are doing conceptually.

@@ -82,7 +80,12 @@ object Eventflow {
fold(err => { println("Error occurred: " + err._2); err._1 }, r => { println("OK"); r._1 })

@SuppressWarnings(Array("org.brianmckenna.wartremover.warts.OptionPartial", "org.brianmckenna.wartremover.warts.ExplicitImplicitTypes"))
val _ = pprint.pprintln(db_, colors = pprint.Colors.Colored)
val _ignore1 = pprint.pprintln(runner1, colors = pprint.Colors.Colored)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

interesting that after some commit in this PR the pprint lost colours :/ (now its just showing toString result)

@svalaskevicius
Copy link
Owner Author

ok, looks a bit better now I hope :)

@Fristi can you spot anything that has to be fixed before merging?

libraryDependencies += "com.chuusai" %% "shapeless" % "2.2.5"

resolvers += Resolver.sonatypeRepo("releases")

addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.6.3")

wartremoverErrors ++= Warts.allBut(Wart.Any, Wart.Nothing, Wart.Throw, Wart.IsInstanceOf, Wart.AsInstanceOf, Wart.NoNeedForMonad)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No more wartremover? :(

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like wartremover.. when it works, but it seems to restrict libraries we can use conveniently at this point.. e.g. uPickle fails quite dramatically and I couldnt find a way to selectively disable it (as it fails when looking for implicits)..

do you have any ideas how can I restore it? (should I use another serialisation library?)


import scala.language.higherKinds

object foldM {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this already in cats somewhere? :)

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sadly no... there is an open PR for it - https://github.com/non/cats/pull/356 but its on hold

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah cool, let's address that later then!

@Fristi
Copy link
Contributor

Fristi commented Nov 6, 2015

Overall I think it's nice to get projections out there! There are some things we could improve here :) Sometimes I find it not easy to digest some of you code. How could we improve on that?
I think with a few modifications we can clarify it a lot, what do you think?

  • Private/public or local functions. If a function does not need public scope, make it local (if you need it for recursive loop or something) or make it a private one if you use it multiple times. This makes modules a bit easier to digest.
  • Don't use tuples in public API's, instead use case classes. They can describe what the values in the tuples are by a good name. I essence case classes are tuples, but with names :)
  • Use descriptive parameter names in case classes and functions. In the example I saw you use parameters names like k, does not really reveal what the intent of parameter is
  • Rather use (implicit e: Backend[T]) for implicits if you use it multiple times. Makes the code more concise (and easier to read), since you don't have to write implictly each time.
  • For concepts like Backend or EventDataConsumerQuery it doesn't reveal it's intention immediately. Would be nice to introduce some docs maybe?
  • Group concepts and put them in packages or separate files.

I can help you out with refactoring some parts! For now it would be good to merge this 👍

@svalaskevicius
Copy link
Owner Author

completely agree! :) in fact, I thought I am already doing most of those items :D apart from short names and no docs.

happy to update the missed parts - in the end, I will have to read this as well even after I forget how it was implemented :)

will have a skim over again and see what have I missed, thanks!

@svalaskevicius
Copy link
Owner Author

@Fristi are these places that you meant - 8a34878 ? happy to improve more if you see something obvious I missed again :)

will merge - but we can always improve this in other PRs (and I hope we will :) )

and thanks for the review!

svalaskevicius pushed a commit that referenced this pull request Nov 6, 2015
add projection example for multiple aggregates
@svalaskevicius svalaskevicius merged commit 344f0bb into master Nov 6, 2015
@svalaskevicius svalaskevicius deleted the multiEventsProjection branch November 6, 2015 17:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants