Package: crudhex
Crudhex⚠️Warn: Beta development stageCLI tool to generate Java CRUD classes from a spec file. The target for this code generation is a Hexagonal architecture.MotivationAdding CRUD operation in a hexagonal project is quite a pain. You can take shortcuts that can be totally legit in cases of just CRUD operations, but most cases if there is already a rich domain/application layer for other use cases CRUD shortcuts can break the consistency of the project.The target of this CLI is to easemy lifeand try to give a general solution to CRUD generations in Hexagonal architecture. There are some customizations you can make, but some aspects for now are closed to customization for now.Getting startedProject config fileProject config file is used to know where things should go in the project. Usually contains a path to the sources folder (src/main/javain regular maven projects) and packages where things are located within that source folder.
Here is a basic example:src:src/main/java# General sources for single module apps, default to src/main/java (maven project)domain-models-pkg:com.salpreh.baseapi.domain.models# domain models packagedomain-commands-pkg:com.salpreh.baseapi.domain.models.commands# domain commands packagedomain-in-ports-pkg:com.salpreh.baseapi.domain.ports.application# domain ports (in/driving/application)domain-out-ports-pkg:com.salpreh.baseapi.domain.ports.infrastructure# domain ports (out/driven/infrastructure)domain-use-cases-pkg:com.salpreh.baseapi.domain.services# domain use cases implementationdomain-exceptions-pkg:com.salpreh.baseapi.domain.exceptions# domain exceptionsdb-adapters-pkg:com.salpreh.baseapi.adapters.infrastructure.db.adapters# db adapters (port implementations)db-models-pkg:com.salpreh.baseapi.adapters.infrastructure.db.models# db entities packagedb-repositories-pkg:com.salpreh.baseapi.adapters.infrastructure.db.repositories# db repositories packagedb-mapper-class:com.salpreh.baseapi.adapters.infrastructure.db.mappers.DbMapper# mapper class to map db adapter entities to domain models. Needed if no mapper option specified when running command (optional)db-mapper-pkg:com.salpreh.baseapi.adapters.infrastructure.db.mappers# db mapper package.Needed when mapper option specified when running command (optional)rest-models-pkg:com.salpreh.baseapi.application.api.models# rest models packagerest-controllers-pkg:com.salpreh.baseapi.application.api.controllers# rest controllers packagerest-exception-handler-pkg:com.salpreh.baseapi.application.api.config# rest exception handler packagerest-mapper-class:com.salpreh.baseapi.application.api.controllers.mappers.ApiMapper# mapper class to map domain models to api models. Needed if no mapper option specified when running command (optional)rest-mapper-pkg:com.salpreh.baseapi.application.api.controllers.mappers# api mapper package. Needed when mapper option specified when running command (optional)In case of multimodule project you will have to specifysrcpath to each module (domain, rest adapter and db adapter).# Source folder for each moduledomain-src:domain/src/main/java# Domain java sourcesdb-adapter-src:infrastructure/datasource-adapter/src/main/java# DB adapter java sourcesrest-adapter-src:application/web/src/main/java# Rest adapter java sources# Packages config as previous exampleMore examples of config can be found in (doc/examples/config).The default name for this config file is.crudhex-conf.yaml, located in the root of the project (you can provide the path to config file by cli options, so you can place it anywhere you want)Spec fileThis is the file where you specify the crud model. Class name, attributes and some metadata about DB structure (relations, PK field, column name alias, etc).An example of spec file:Person:# Model name.meta:table-name:persons# OPTIONAL: Table name for entityid:# Field nametype:Longid:sequence# PK marker. This field will be the primary key for the entity.# As value, you specify generation strategy available in JPA with lower case.name:String# Field name and typebirthPlanet:# Field nametype:Planetcolumn:birth_planet# OPTIONAL: Column name aliasrelation:# DB relation meta data in case of FKtype:many-to-one# Relation typejoin-column:birth_planet_id# Join column for relationaffiliations:type:Factionrelation:type:many-to-many# In case of many-to-many we have a couple of more meta about DB setupjoin-table:person_affiliationjoin-column:person_idinverse-join-column:faction_idbackup:type:Personrelation:type:one-to-onejoin-column:backup_idbacking:type:Personrelation:type:one-to-onemapped-by:backup# Mapping attribute for non-owning side of relation# Here another modelPlanet:.meta:table-name:planetsid:type:Longid:# You can specify the generation strategy as map to use additional options. In this case we are giving a sequence nametype:sequencesequence:planet_pk_genname:# Field name and type also can be specified as map to add column name metadatatype:Stringcolumn:original_nameaffiliation:type:Factionrelation:type:many-to-onejoin-column:affiliation_idrelevantPersons:type:Personrelation:type:one-to-manymapped-by:birthPlanetSnippet config comments provide an overview of each relevant section in config. I'll dig further in config details in a dedicated section.
For now, you can find some additional examples indoc/examples/spec.InstallationPackage is published in Pypi public repositories. You can usepip(or another convenient python dependency manager) to install the package:pipinstallcrudhexOnce installed you should be able to use it as CLI tool:crudhex--helpUsageYou can use the--helpoption to see all available options:crudhex--helpTo generate the code you need to locate the shell in the root of the project and run the command:crudhex-mmapstructspec/crudhex.yamlHere we are specifying mapstruct as mapper library and the path to the spec file. By default, will use the config file.crudhex-conf.yamllocated in the root of the project.If you want to use a different config file, you can specify it with the-coption:crudhex-c.doc/config/cruhex-config.yamlspec/crudhex.yamlDetailsSpec file optionsModelFirst level keys in spec file are themodel names. Each model name is followed by a map with the model fields/attributes.Person:# Model nameid:Long# Field namename:String# Field namesurname:# Field nametype:Stringcolumn:last_namebirthPlanet:# Field name with relation datatype:Planetcolumn:birth_planetrelation:type:many-to-onejoin-column:birth_planet_idThere is a special key inside the model map called.metathat contains metadata about the model.Person:# Model name.meta:table-name:persons# Table name for entityid:Long# Field namename:String# Field name# More fieldsCurrent supported options in meta are:table-name: Table name for the entity. If not specified, will use the model name as table name.FieldFields in the model have many options depending on the type of field. The most basic field spec is just the field name and the type:Person:# Model nameid:Long# Field namename:String# Field nameThis structure can be expanded to specify additional options for the field.Person:# Model namename:# Field nametype:String# Field typecolumn:original_name# Column name aliasid:# Field nametype:Long# Field typeid:type:sequence# PK generation strategysequence:person_pk_gen# Sequence namebirthPlanet:# Field nametype:Planet# Field typecolumn:birth_planet# Column name aliasrelation:# DB relation metadatatype:many-to-onejoin-column:birth_planet_idOptions available are:type: Field type. Can contain a primitive, java type, custom class or another model.column: Column name alias. If not specified, will use the field name as column name.id: Marks the field as Primary key in DB. Also contains metadata for PK generation strategy.relation: Contains metadata for DB relations.Expanding ontypeoptions available here is an example with each one of the mentioned options:Person:id:type:UUID# Java typeid:autoage:int# Primitive typebirthPlanet:type:Planet# Model classrelation:type:many-to-onejoin-column:birth_planet_idrace:com.salpreh.baseapi.domain.models.RaceType# Custom classFor custom classes full package name is required (racefield in example). For java types all jdk 11 classes are supported, in case you need a not supported type you can use full package name to refer to it. There is a list of supported types incrudhex/domain/services/data/class_type_data.py.For regular fields most common option is to use type directly as value (first example), or a map with type and column name alias (second example). We will dig intoidandrelationoptions in the next sections.Id fieldsId fields are marked with theidkey. This key also contains metadata about the PK generation strategy.Person:# Model nameid:# Field nametype:Long# Field typeid:type:sequence# PK generation strategysequence:person_pk_gen# Sequence nameSpaceship:# Model namecode:# Field nametype:UUID# Field typecolumn:ship_code# Column name aliasid:auto# PK generation strategyPK generation strategies are mapped to JPA generation strategies with lower case. Current supported strategies are:none: No generation strategy.auto: Auto generation strategy.sequence: Sequence generation strategy. Sequence name can be provided.identity: Identity generation strategy.Relation fieldsRelation fields are marked with therelationkey. This key also contains metadata about the relation.Person:birthPlanet:# Field nametype:Planet# Field typecolumn:birth_planetrelation:# DB relation metadatatype:many-to-one# Relation typejoin-column:birth_planet_id# Join column for relationaffiliations:type:Faction# Field typerelation:# DB relation metadatatype:many-to-many# Relation typejoin-table:person_affiliation# Join table for relationjoin-column:person_id# Join column for relationinverse-join-column:faction_id# Inverse join column for relationbackup:# Field nametype:Person# Field typerelation:# DB relation metadatatype:one-to-one# Relation typejoin-column:backup_id# Join column for relationOptions available are:type: Relation type. Supported options are:one-to-oneone-to-manymany-to-onemany-to-manyjoin-column: Join column for relation (owning side).mapped-by: Mapped by field for relation (inverse side).inverse-join-column: Inverse join column for relation (many-to-many relations).join-table: Join table for relation (many-to-many relations).All of these options are mapped to JPA annotations and configurations. Explaining when to use each option and what it does is out of the scope of this document.AnnexesReferencesJPA Bidirectional relationsJPA many-to-many relationsJPA equals/hashCodeJPA many-to-many collections