Skip to content

Simplification

Choose a tag to compare

@olger olger released this 17 Feb 14:49
· 11 commits to main since this release
dda354b

This release removed a lot of functionality that should not be part of bounded but was more generic for building applications.

THIS RELEASE IS REMOVES FUNCTIONALITY

please read the following to see how you are affected.

Things that were done:

  • Removed all additional required elements for Commands and Events.
  • Made the event materializer filter more generic in use. Removed all build and runtime requirement handling.
  • Remove compiler warnings and cleanup
  • Removed explicit use of Spray-JSON
  • Made version ready for release as 0.3.0

##Migration

As Runtime, BuildInfo, Compatible materialiser filter and the required use of Spray JSON are removed you need to:

The Command and Event structure changed to minimal required fields and methods.

This means that you can decide if, when and what metadata you like to use.
If you need compatibility, you need to define:

  trait Id {
    def idAsString: String
  }

  trait UserId extends Id

  trait AggregateRootId extends Id

  trait UserContext {
    def userId: UserId

    def roles: List[String]
  }

  trait CommandMetaData {
    def timestamp: OffsetDateTime

    def userContext: Option[UserContext]

    val commandId: UUID = UUID.randomUUID()
  }

  trait MetaData {
    def timestamp: OffsetDateTime

    def userContext: Option[UserContext]

    def causedByCommand: Option[UUID]
  }

Please note that you are free to make them more application specific as there is no dependency on bounded for these.

  • The AggregateRootId is now a String

Spray JSON is not required

Some helpful serialisers were written for serialising events and could also be used in your application.
If you used those, you need to add:

  def jsonEnum[T <: Enumeration](enu: T): JsonFormat[T#Value] =
    new JsonFormat[T#Value] {
      def write(obj: T#Value): JsValue = JsString(obj.toString)

      def read(json: JsValue): T#Value =
        json match {
          case JsString(txt) => enu.withName(txt)
          case something =>
            throw DeserializationException(s"Expected a value from enum $enu instead of $something")
        }
    }

  implicit object OffsetDateTimeJsonFormat extends RootJsonFormat[OffsetDateTime] {

    def write(dt: OffsetDateTime): JsValue =
      JsString(
        dt.truncatedTo(ChronoUnit.SECONDS)
          .format(DateTimeFormatter.ISO_DATE_TIME)
      )

    def read(value: JsValue): OffsetDateTime =
      value match {
        case JsString(v) =>
          OffsetDateTime.parse(v, DateTimeFormatter.ISO_DATE_TIME)
        case _ =>
          deserializationError(s"value $value not conform ISO8601 (yyyy-MM-dd'T'HH:mm:ssZZ) where time is optional")
      }
  }

  implicit object JavaUUIDFormat extends RootJsonFormat[UUID] {
    override def write(obj: UUID): JsValue = JsString(obj.toString)

    override def read(json: JsValue): UUID =
      json match {
        case JsString(v) => UUID.fromString(v)
        case _ =>
          deserializationError(s"value $json cannot be deserialized to a UUID")
      }
  }

Serialisation now works with the new serialiser structure like jackson-json

Your application.conf needs to have a different section for the akka.actor

  actor {
    serialize-creators = off
    serialize-messages = off
    serialization-bindings {
      "io.cafienne.bounded.aggregate.DomainEvent" = jackson-json
      // enable below to check if all taskEvents have been serialized without java.io.Serializable
      "java.io.Serializable" = none
    }
  }

Removal of BuildInfo, RuntimeInfo and Compatibility in the event materializer

You can define your own classes or remove them from your application.