ContextMapper / context-mapper-dsl

ContextMapper DSL: A Domain-specific Language for Context Mapping & Service Decomposition
https://contextmapper.org/
Apache License 2.0
223 stars 30 forks source link

Annotation in ContextMapper #278

Closed sunilk-m closed 1 year ago

sunilk-m commented 3 years ago

Annotation provide the ability to extend the language without having to modify the grammer and can be very valuable for generating code from the model by providing the appropriate guidance to the generator. It should be possible to add annotation to any element in the model. The example below describes in more detail. From the model we can provide the annotation as shown below. The @Table/@Document can be used on Entity and if JPA classes are generated then it can be used to drive the generator to generate the appropriate annotation on the JPA class. Similarly the @Unique annotation can be used to mark an attribute as unique. The corresponding attribute in the generated JPA class will be marked as unique. To define a combination of unique attribute we can use @Unique annotation with the same name.

BoundedContext VendorManagement { .... Aggregate Vendor { @Table/@Document(name="vendors") Entity Vendor { String id key @Unique(name= "") String name String description } } ... }

The corresponding generated JPA class might look like this in case of table @Entity public class Vendor { ... @Id private String id; @Column(unique=true) private String name; private String description; ... }

The corresponding generated JPA class might look like this in case of Document (eg Arangodb) @Document("vendors") public class Vendor { ... @Id private String id; private String name; private String description; ... } These are basic examples, in case of graph db storage we can also define entities as nodes and edges to define the vertices and edges by using appropriate stereotypes.

In order to support annotations, context mapper grammer will have to be enhanced to define annotations on each of the modeling elements. The supported annotation should be defined separately in a different file which can be reused across teams and can also be used to validate the usage. The annotation can be defined using the following format <name > This information can be used for validations also. An option should also be provided to hide the annotation by default and can be displayed only if user is interested in viewing those details Alternative is to specify this externally as additional metadata information to guide the generator but this leads to duplication which can be avoided by providing support in the tool itself.

stefan-ka commented 3 years ago

Hi @sunilk-m, thanks for reporting!

I'm not sure if we should go for such a generic solution since the tactic DDD syntax is based on Sculptor and already supports many of your needs (let me know if I'm wrong).

It is possible to define the database table name as follows:

Entity Customer { 
    aggregateRoot

    databaseTable = "Customer" // see here

    String firstname
    String lastname
}

... its also possible to define the column name on attribute level:

Entity Customer { 
    aggregateRoot

    databaseTable = "Customer" // see here

    String firstname databaseColumn = "firstname"
    String lastname databaseColumn = "lastname"
}

As you can see here the attribute rule supports other keywords such as required, nullable, notChangeable, etc. The only thing that is not available is unique.

Maybe we could just add a corresponding unique keyword? Would that be sufficient or do you have other requirements that are missing @sunilk-m? I get the idea of the generic solution that would allow adding any arbitrary additional attributes but I want to challenge if its really needed. What do you think @socadk?

sunilk-m commented 3 years ago

Hi Stefan Thanks for your reply. I will add one more scenario that I am currently working on. I have a read model which needs to be persisted into a graph database and I am currently on evaluating arangodb. In this case I have 2 types of entities i.e vertices & edges where vertices are nodes in the graph and edges connect the nodes. So in context mapper if I have to create such a model, I need some way of marking some entities as vertices and others as edges. The workaround I have today is to use the databaseTable attribute and hint to provide this additional meta information. For example if I have to model airports and the connecting flights information using graphs I will do something like this. Entity Airport { aggregateRoot

 databaseTable = "type=VERTEX, name=AIRPORT"

String name .... } Entity Flight { databaseTable = "type=EDGE, name=FLIGHTS" -Airport from hint="FROM" -Airport to hint ="TO" }

My primary use case for using context mapper is to capture the domain model and also use that model to drive generation of code so that right patterns and best practices can be enforced. In order to generate code I will need some additional meta information and that's the reason I was proposing a generic solution. The solution I proposed is widely used in case of code generation using UML and I have myself used it a lot of times in the past.

So my answer to the question " already supports many of your needs (let me know if I'm wrong)." is - it supports through workarounds but it would be great if a uniform approach is available supported by the tool :).

Regards Sunil

On Fri, Jan 15, 2021 at 2:25 AM Stefan Kapferer notifications@github.com wrote:

Hi @sunilk-m https://github.com/sunilk-m, thanks for reporting!

I'm not sure if we should go for such a generic solution since the tactic DDD syntax is based on Sculptor http://sculptorgenerator.org/ and already supports many of your needs (let me know if I'm wrong).

It is possible to define the database table name as follows:

Entity Customer { aggregateRoot

databaseTable = "Customer" // see here

String firstname String lastname }

... its also possible to define the column name on attribute level:

Entity Customer { aggregateRoot

databaseTable = "Customer" // see here

String firstname databaseColumn = "firstname" String lastname databaseColumn = "lastname" }

As you can see here https://github.com/ContextMapper/context-mapper-dsl/blob/d4ae1ba982702ac48773de968d9558a9eb22fbec/org.contextmapper.dsl/src/org/contextmapper/tactic/dsl/TacticDDDLanguage.xtext#L365 the attribute rule supports other keywords such as required, nullable, notChangeable, etc. The only thing that is not available is unique.

Maybe we could just add a corresponding unique keyword? Would that be sufficient or do you have other requirements that are missing @sunilk-m https://github.com/sunilk-m? I get the idea of the generic solution that would allow adding any arbitrary additional attributes but I want to challenge if its really needed. What do you think @socadk https://github.com/socadk?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/ContextMapper/context-mapper-dsl/issues/278#issuecomment-760470185, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANHNC3L6M4MCKAJZSMQM7UDSZ5K3PANCNFSM4V5N5IZQ .

socadk commented 3 years ago

Hi, while I can see the need for such generic solution/uniform approach, I agree with @stefan-ka 's reasoning above.

Sculptor also has the docstrings (doc=STRING)? that can carry such information (note: its originally indented usage is different, see here).

I do like the proposal for a new unique keyword, though.

stefan-ka commented 3 years ago

@sunilk-m

FYI: the unique keyword is now supported (with release 6.4.0).

StevenVanDijk commented 2 years ago

I implemented the suggestion of @socadk to use the doc statements. This CML:

BoundedContext CustomerManagementContext {

    Aggregate Customers {
        Entity Customer {
            aggregateRoot

            "lives at" - @Address address

            * void anotherMethod("uses" @Name name);
            "needs something" * @ReturnTypeEntity yetAnotherMethod();
        }

        Entity Address {
            String name
        }

        ValueObject Name {
            String first
            String last
        }

        Entity ReturnTypeEntity {
            int i
        }
    }
}

generates this PUML:

@startuml

skinparam componentStyle uml2

package "'Customers' Aggregate" <<Rectangle>> {
    class Customer <<(A,#fffab8) Aggregate Root>> {
        Address address
        void anotherMethod(Name name)
        ReturnTypeEntity yetAnotherMethod()
    }
    class Address <<(E,DarkSeaGreen) Entity>> {
        String name
    }
    class Name <<(V,DarkSeaGreen) Value Object>> {
        String first
        String last
    }
    class ReturnTypeEntity <<(E,DarkSeaGreen) Entity>> {
        int i
    }
}
Customer --> Address : lives at
Customer --> Name : uses
Customer --> ReturnTypeEntity : needs something

@enduml

I.e. image

This is exactly what I need (i.e. the ability to annotate relations between entities in a way that is readable for non-technical domain experts).

The syntax is a bit peculiar like this, but it works. What do you think? Should I prepare a pull request?

StevenVanDijk commented 2 years ago

Sorry, reading back I see that @sunilk-m's usecase was rather different. I have no interest in code generation, I am solely using context-mapper as a modeling tool. As such I found it useful to make the generated diagram more naturally readable. How to proceed?

stefan-ka commented 2 years ago

Hi @StevenVanDijk

Thank you very much for your work! I like the idea... Would be nice to have a PR so we can also play around with it a bit. Could you create one?

What do you think @socadk?

StevenVanDijk commented 2 years ago

Here is the PR: https://github.com/ContextMapper/context-mapper-dsl/pull/319

stefan-ka commented 1 year ago

@StevenVanDijk, we released v6.7.0 today which includes your contribution ;) https://contextmapper.org/news/2022/12/02/v6.7.0-released/ Thanks again for this! Therefore, I'll close this issue now. Best regards, Stefan