xmolecules/jmolecules

Provide a way to specify a reference to identity using annotation

zambrovski opened this issue · 5 comments

Short

Introduce a new stereotype IdentityRef expressing a reference to a Identity as annotation.

Description

I stumbled upon an interesting question implementing command dispatching in CQRS. The command model accessed via aggregate root needs to be able to build an identifier from the context of a command to target the correct aggregate / entity.

A very easy way to target is to specify the target identifier inside the command. Firstly, I thought I could use Identifier or @Identity from jmolecules-ddd for this purpose, but it seems to be very strange... The command itself is an immutable value object and putting a field inside of it marked with @Identity seems to be confusing... A better approach is to use a different stereotype IdentityRef (as a annotation) to express the "pointer-to-identity" semantics.

Look on the follow example:

@Command(namespace = "customer", name = "UpdateCustomerProfile")
data class UpdateCustomerProfileCommand(
  @IdentityRef
  val customerId: CustomerId,
  val firstName: FirstName,
  val lastName: LastName
)

@AggregateRoot
class CustomerProfile {

  @Identity
  lateinit var customerId: CustomerId

  @CommandHandler(namespace = "customer", name = "UpdateCustomerProfile")
  fun handle(command: UpdateCustomerProfileCommand) {
    // ...  
  }
}

Is the Association marker interface is exactly designed for this? Then I think the definition of the association with an annotation should be possible:

/**
 * Marks the reference to identity.
 * Carries optional the identity type and the identifiable type.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Documented
public @interface IdentityRef {

    /**
     * Defines identity type.
     *
     * @return class defining the identity type.
     */
    Class<?> identityType() default void.class;

    /**
     * Defines the aggregate type.
     *
     * @return class defining the identifiable type (entity or aggregate).
     */
    Class<?> identifiableType() default void.class;
}

You're right. In the type-based model, Association is what would model this, if you wanted to go beyond declaring the simple identifier field. I guess we could introduce an Association annotation, too, to keep symmetry between the two models (we already have that for AggregateRoot, Entity etc.).

What exactly would the purpose of the identityType attribute be? Isn't that – by definition – the type of the field?

I agree with everything you wrote. You are absolutely right, the identityType is not required... Should I provide a PR then? How the annotation should be called? Are you ok with Association` then?

To tell you what I'm currently building: it is Axon Framework byte buddy integration. For support of Axon Framework @TargetAggregateIdentifier I would use the @Association annotation then...

I agree with everything you wrote. You are absolutely right, the identityType is not required... Should I provide a PR then? How the annotation should be called? Are you ok with Association` then?

I would happily have a look at a PR. Association is just fine. I am wondering whether we want to be consistent with @Identity in allowing both annotations on methods, likely the getter, too.

To tell you what I'm currently building: it is Axon Framework byte buddy integration. For support of Axon Framework @TargetAggregateIdentifier I would use the @Association annotation then...

Nice! Looking forward to seeing that PR as well!

#82 has been polished and merged.