-
Notifications
You must be signed in to change notification settings - Fork 106
The Stereotype Metamodel
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
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.
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
andtypes
segments from it. The identifier for aggregate root would beorg.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:
- How to identify a type or annotation that is a concrete stereotype.
- The ability to customize the properties in cases in which the defaults don’t fit.
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 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”.
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, …
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.
- 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?