Skip to content

The Stereotype Metamodel

Oliver Drotbohm edited this page Jan 16, 2025 · 1 revision

jMolecules defines a set of annotations and interfaces that represent design and architectural stereotypes that allow assigning classes a meta-role. These stereotypes constitute languages, such as the Domain-Driven Design building blocks, Hexagonal Architecture etc.

End user applications often establish application-specific stereotypes, and we would like to allow seamless extension of the stereotype model and an API that allows consistently looking up stereotype information from arbitrary classes across both predefined and custom stereotypes.

A metamodel is useful because…

  • … it allows the definition of a canonical stereotype, potentially represented by both an annotation and an interface. For the DDD building blocks, both variants exist.

  • … it allows the definition of new stereotypes in the user space but a canonical lookup of both the predefined stereotypes and

The Stereotype abstraction

We introduce a Stereotype and StereotypeFactory abstraction in jMolecules integrations. Stereotype exposes properties surrounding stereotypes and defines rules how these properties are supposed to be derived from types, packages, and methods. StereotypeFactory is generically typed so that it can be implemented for different type abstractions, such as the standard Java reflection API, ArchUnit’s type API, APT, IDE-specific abstractions etc.

An Example

Let’s take the jMolecules DDD aggregate root stereotype as an example. It’s currently represented by both the org.jmolecules.ddd.annotations.AggregateRoot and the org.jmolecules.ddd.types.AggregateRoot interface. For that concept, an instance of Stereotype would expose the following properties.

  • An identifier that allows to uniquely reference the stereotype. To bridge the world of annotation and interfaces-based declarations, we’d use the fully qualified type name, but strip annotations and types segments from it. The identifier for aggregate root would be org.jmolecules.ddd.AggregateRoot.

  • A display name that is supposed to be used in user-facing display. This should be derived from the simple class name, split at the CamelCase segments. The default display name for aggregate root would be “Aggregate Root”.

  • A logical group that groups different stereotypes so that they can form languages. The group would default to the identifier but without the local name. The group for aggregate root would be org.jmolecules.ddd.

  • A numeric priority that, in case of a type having assigned multiple stereotypes to create a hierarchy, to ultimately define a primary stereotype among all assigned. An aggregate root is also an entity, which in turn is also an identifiable. By default, the priority is 0.

With the properties and their definition in place, we still need to tackle two problems:

  1. How to identify a type or annotation that is a concrete stereotype.
  2. The ability to customize the properties in cases in which the defaults don’t fit.

The @Stereotype Annotation

To solve both problems, we are going to introduce an @Stereotype meta-annotation and add that to all annotations and interfaces we define. It will expose the properties described above as annotation attributes.

A Custom Example

A custom annotation-based stereotype would look like this:

package com.acme.example.stereotypes;

@org.jmolecules.Stereotype
@Retention(RUNTIME)
public @interface MyCustomStereotype { … }

The same stereotype could also be defined as interface

package com.acme.example.stereotypes;

@org.jmolecules.Stereotype
public interface MyCustomStereotype { … }

An application component would then get the stereotype assigned as follows

@com.acme.example.stereotypes.MyCustomStereotype // <- either or …
class MyApplicationComponent implements MyCustomStereotype { … }

This would result in MyApplicationComponent being assigned the MyCustomStereotype, with an identifier of com.acme.example.stereotypes.MyCustomStereotype, a group of com.acme.example.stereotypes and a display name of “My Custom Stereotype”.

Implementation Considerations

Stereotype Discovery

To discover stereotypes, the following algorithm should be used. We need to find the @Stereotype annotation on a type. The type found then is subject to metadata derivation. Given a type T, …

IDE Considerations

IDEs usually work with a custom abstraction about the types and packages of projects they’re dealing with. That said, their primary integration with the idea described here would be to look out for the @Stereotype annotation in types declared in the user project and use those for grouping and displaying types in dedicated views.

Ideas / Open Questions

  • Does it make sense for a stereotype to belong to multiple groups? Spring’s @Controller might be assigned to the “web” group, but also to “spring”?
  • Does it make sense to be able to define metadata such as a display name for a stereotype group?