jhipster / jhipster-core

JHipster Domain Language, used by JHipster UML and JDL-Studio to generate entities
Apache License 2.0
345 stars 114 forks source link

Enriching the JDL grammar #141

Closed MathieuAA closed 6 years ago

MathieuAA commented 7 years ago

From the generator's original post here

Syntax in consideration:

commonConfig.jdl

BASE_PORT 8080 // common constants

application abstract BaseApp { // Implies that the app is abstract and will not be generated
  config {
    groupId "org.comp.proj"
    authenticationType "jwt"
    cache "hazelcast"
    serviceDiscoveryType "eureka"
    buildTool "maven"
    serverPort BASE_PORT
    packageName "org.comp.proj"
  }
}

entity abstract A {} // Implies entity is abstract and will not be generated unless referred to from an application
entity abstract B {}
entity abstract C {
    String name
}

service abstract AService { // Implies service is abstract and will only be generated when referred from an application
    Customer findByName(String name)
}

myMicroservice1JDLFile.jdl

import commonConfig.jdl             // import common configuration

application MyMicroservice1 {
  type 'microservice'
  config extend BaseApp {           // extend base app config
    serverPort (BASE_PORT + 100)
    databaseType "mongodb"
  }

  entity MyOnlyEntity extends C {     // one entity of the service extends one from commonConfig.jdl
    // this will have the field name generated as it extends entity C
    String code
  }

  services AService      // refers to external services from commonConfig.jdl
  service MyOnlyService {
    Customer findByName(String name)
  }

  paginate * with pager
  service * with serviceImpl
}

myGateway.jdl

import commonConfig.jdl             // import common configuration
import myMicroservice1JDLFile.jdl   // import microservice 1
import myMicroservice2JDLFile.jdl   // import microservice 2 and so on
import dockerConfig.jdl             // import docker compose setup

application MyGateway {
  type 'gateway'
  config extend BaseApp {           // extend base app config
    databaseType "Postgresql"
    clientFramework "react"
  }

  entities A, B                       // refers to external entities from commonConfig.jdl
  entity MyOnlyEntity extends C {     // one entity of the gateway extends one from commonConfig.jdl
    // this will have the field name generated as it extends entity C
    String code
  }

  relationship ManyToMany {
      A{b} to B{a}
  }

  services AService      // refers to external services from commonConfig.jdl
  service MyOnlyService {
    Customer findByName(String name)
  }

  paginate * with infinite-scroll
  service * with serviceImpl
}

This will generate

├── my-gateway
│   ├── pom.xml
│   └── src
│       └── main
│
├── my-microservice-1
│   └── pom.xml
|
├── my-microservice-2
│   └── pom.xml
├── docker-compose 
colameo commented 7 years ago

Please keep in mind, that changes in the JDL grammar affects the community which uses jhipster-ide. Since the ide features were introduced we have 300-500 installation per month and as you can imagine a change in the JDL would affect those people...

Beside of that, I also like the idea to improve the JDL grammar ;-) and thought to implement a kind of "Xjdl" (which stands for eXtended JDL) with feature similar to what you discussed in the issue #6275.

So I'm looking forward to this...

deepu105 commented 7 years ago

Colameo, the changes will be mostly backward compatible I believe

MathieuAA commented 7 years ago

Yes. The only breaking change I've ever made was about regexes (the separator is now / instead of ').

colameo commented 7 years ago

@MathieuAA - ok that's good, that means we do not have pressure to integrate all changes in sync with yours.

agoncal commented 7 years ago

@deepu105 I liked the abstract and the type microservice, this leaves other possibilities.

Deepu, I didn't get the difference between services (plural) and service singular in your post : https://github.com/jhipster/generator-jhipster/issues/6275#issuecomment-325188239. You wrote entity MyOnlyEntity (singular) and services MyOnlyService (plural). Is this a typo ?

 entity MyOnlyEntity extends C {     // one entity of the service extends one from commonConfig.jdl
    // this will have the field name generated as it extends entity C
    String code
  }

  services MyOnlyService {
    Customer findByName(String name)
  }
MathieuAA commented 7 years ago

@agoncal it must be. @jhipster/developers, @bd82, @hakandilek My main goal this week will be to get the app generation up and running. This means being able to generate apps from one or more JDL files, without generating entities right after (this comes after).

@deepu105 Do you think it will be easy to update the subgen to make it generate an application?

deepu105 commented 7 years ago

@MathieuAA yes the import-jdl sub gen can be easily updated to compose the app sub gen based on the app configuration/yo-rc generated. Unfortunately, I'll be on vacation from tomorrow until 8th of September so I won't be of much help. I can, of course, review things from my phone. But this should be straight forward just follow the same way entity generator is composed.

@agoncal might be a typo, I meant singular/plural in the same meaning as you guys did. Also I would prefer to start with service creation and not repository as we already have a service sub gen to handle and also as per our policies it brings more value(repository can be easily hand created as its just adding method to an interface) for service we could create the service skeleton, a REST interface for that and so on. I guess you will achieve the same result that you expected with the repository keyword

deepu105 commented 7 years ago

@bd82 this is the autocomplete snippet we use. https://github.com/jhipster/jdl-studio/blob/gh-pages/js/codemirror/codemirror.jdl-mode.js

MathieuAA commented 7 years ago

@deepu105 thanks for adding the syntax! I'll work on it this week and submit a PR.

deepu105 commented 7 years ago

@MathieuAA take look at the PR from @bd82 before you start as it might be useful

MathieuAA commented 7 years ago

@deepu105 Yeah I've had a look yesterday, and I agree with you

agoncal commented 7 years ago

@deepu105 I was thinking of your idea of "inheritance".

If you have a repository :

repository MyRepo {
 int countByAge(int age);
}

With this code, you have a valuable repository (valuable, because you can use it and it works), it's associated service and REST controller. Everything works.

Now, for a service:

service MyService {
 String myMethod(int param);
}

What do you do with it ? What do you generate ? And empty method ? What for ? You would have to add code on it. Coming back to your idea of inheritance, what about generating the following code:

public class MyService {
  public abstract String myMethod(int param);
}

public class ExtendedMyService extends MyService {
  @override
  public String myMethod(int param) {
    // add your code
  } 
}

This would allow to regenerate JHipster code easily (here, the MyService class) and creates an extension point for developers (using the ExtendedMyService).

WDYT ? Is that what you had in mind ?

deepu105 commented 7 years ago

@agoncal overall I would like to move to an Inheritance model for our generated code so that people can update the child classes without having to worry about being overwritten by upgrades and stuff.

Now for the repository vs Service. I belive if you generate a repo with say

repository MyRepo {
 int countByAge(int age);
}

You would create a Spring Data Interface called MyRepo with a method on it called countByAge and you could generate a Spring service class called MyRepoService with method countByAge which calls MyRepo.countByAge and a Rest Resource called MyRepoResource with method countByAge which calls MyRepoService.countByAge (I hope this is what you meant) IMHO this is not very valuable as it wont take much effort or time to create the same using an IDE. Exact same classes and methods can be generated by below as well

service MyService {
 String myMethod(int param);
}

What I would rather prefer would be to be more flexible and provide a way to create service (as repository is too easy to hand code, just a spring data interface with methods) so you can do something like

service MyService {
 String myMethod(int param)
 Int countByAge(int age)
}
decorate MyService with repository, resource, serviceImpl

This will generate a resource class with end point for both methods (GET by default) the methods will call corresponding methods from service, an interface and impl for the service which will call repository methods. A spring repository. Here though its not too much value its faster to create different combinations and then customize it. And yes inheritance model would be nice on it as well

agoncal commented 7 years ago

@deepu105 I agree that you can create Spring Data interface + service + REST controller using an IDE... but being able to do it in JDL is important.

The way I work with JHipster is that I never touch the generated code, and only use inheritance. This allows me to upgrade my projects. In in my JDL I have a :

repository MyRepo {
 int countByAge(int age);
}

I know it will be in the generated code, and I know I won't have to touch it when upgrading JHipster. If you think of JHipster being an "IDE code generator", then, I can either use JHipster to generate a repository+service+controller or the IDE.

Services is where the business logic happens:

service MyService {
 double myComplexCalculation()
}

With this code, there is not much you can do. For my model of programming (inheritance) this would not help me.

You idea of decoration is great. You could even go:

entity MyEntity {
 String name
}
decorate MyEntity with repository, resource, serviceImpl
bd82 commented 7 years ago

@deepu105 , everyone.

How large of an enrichment of the grammar are we talking about here?

I'm trying to figure out how the questions of:

If most of the Grammar will effectively be redesigned, we may want to answer the "How?" question first to avoid duplicate work.

If this enrichment mostly affects things outside the grammar and there will only be a moderate amount of syntax changes perhaps it does not matter and the tracks can be executed independently and in parallel.

agoncal commented 7 years ago

Another idea is also to be able to generate Enums that are not related to Entity (see https://github.com/jhipster/jhipster-core/issues/167)

taurus227 commented 7 years ago

This part of the original issue was not brought over, so I copied it here:

Uniqueness

Today we have several validations on our attributes (required, minlength, maxlength, pattern). We could have unique.

entity MyEntity {
  myCode String unique,
  myField String required,
  mySecondField String
}

This would generate a @Column(unique = true) as well as a Liquibase constraint :

  <addUniqueConstraint columnNames="code"
  tableName="mytable"
  constraintName="my_unique_code"/>

Issue #18 also proposed unique constraints, but it actually went one step further, proposing a way to specify multi-column unique constraints, like so:

unique { entityA{columnA, columnB} }

This could then generate the composite unique constraint in Liquibase.

If you agree that this would be useful, I'd be happy to contribute this feature. However, being a jhipster rookie, could somebody point me in the right direction, like what are the parts that would need modifications?

MathieuAA commented 7 years ago

All, I agree to the unique thing as long as it doesn't look like unique { entityA{columnA, columnB} }. If we implement it, we'd have to have this syntax for every field validation, and it just isn't right. A field validation is only relevant to a field, its field, not the class or the file. The syntax should be:

entity A {
  mySuperImportantField String required unique,
  myNotSoImportantField Integer max(42),
  myOtherImportantField unique
}

If it's implemented in the generator and the other JHipster devs agree to it (incidentally a PR for this would be nice in the generator), I'll add it to the JDL.

Now for this, a new issue should have been created (or posting in the first one) instead of adding yet another topic to this already impressive thread.

deepu105 commented 7 years ago

I guess we were clear in another thread that we won't afd unique validation as there is no added value. So for now it is out of scope lets not do it. Lets finish the basic grammar we agreed above and later we can think of other enhancements

agoncal commented 6 years ago

@MathieuAA just tried JDL v2 : well done !

deepu105 commented 6 years ago

@agoncal did you try JDL v3? with the microservice stack generation https://github.com/jhipster/generator-jhipster/pull/8335

agoncal commented 6 years ago

@deepu105 Current project is a monolith with Keycloak. Haven't had the chance to use microservices in v3... but will certainly give it a try ;o)

antarus commented 6 years ago

Hello, @MathieuAA I need the notion of extends andabstract for entities at work. I will be able to watch the generator part of this feature. But I have some question:

Tcharl commented 6 years ago

Let me disturb that conversation a little ^^ ;-).

If the goal of this new syntax is to ease JH upgrade without modifying code, it may be wise to focus on other aspects. If we take generated elements from the ground up:

May be adding a notion of extensibility on entities could be nice?

@sidebyside {
Service,
Repository,
Resource,
ClientService
}
entity ExtendedEntity {}

Or even better being inspired by the EMF XCore like or VPDSL syntax

@generation {
Entity(skip), //skip|true defaults to true
Repository ([{jpa: decorate}, {search: true}], //skip|true|decorate defaults to true
DTO({mapstruct: decorate}), //skip|true|decorate defaults to true
Service (impl, decorate), //impl|class|decorate defaults to class
Resource(decorate), //true|decorate defaults to true
ClientService(decorate) //true|decorate defaults to true
}
entity ExtendedEntity {}

With one or the other, we could avoid the 'repository' and 'service' keyword which are in this form kind of overkill for what they achieve IMHO.

Also, I really like the notion of generic definition extension and abstraction, which would be very useful in many cases, circumventing the actual impossibility to have the same entity in two microservices which is frequent when doing client-server.

I'm finally inline with the point of cbornet: we should be able to host a full microservice definition by jdl file, which is impossible today, due to the generic configurations like search, skipClient, and so on. May be a little trick concatenating every imported jh values for a property could do the trick.

deepu105 commented 6 years ago

I have been working on some JDL syntax and feature enhancements. Ill share them soon, for the record I don't want to change any existing syntax, I think they are quite simple and nice, which is more important. Let me write up about what I have in mind later.

deepu105 commented 6 years ago

closing in favor of https://github.com/jhipster/jhipster-core/issues/277