hashgraph / hedera-services

Crypto, token, consensus, file, and smart contract services for the Hedera public ledger
Apache License 2.0
313 stars 138 forks source link

Platform Modularization #6611

Open hendrikebbers opened 1 year ago

hendrikebbers commented 1 year ago

Currently the layout of the modules in platform is slightly different to the layout in the modules of services. We already defined a lot of best practices in services and it would be great if we can have a common pattern for all modules. This epic defines the general structure that a module should have.

Information: for services a short documentation about the layout of modules already exists that is sadly not up to date: https://github.com/hashgraph/hedera-services/blob/develop/hedera-node/docs/design/modules.md

Internal structure of a module

Since our project is using Gradle as build tool all our modules are Gradle modules. Next to this mostly all modules are Java modules.

Gradle module description

Each module needs a build.gradle.kts file that describes the module.

General best practices for all our (Java) modules are defined in custom plugins that can be found under buildSrc/src/main/kotlin. For a Java module the com.hedera.hashgraph.javaConventions plugin should be used. Next to this each module should have a description. Since nothing else is needed for a minimal module the most simple build.gradle.kts looks like this:

plugins {
id("com.hedera.hashgraph.javaConventions")
}

description = "A minimal module without any dependecies"

The group, name and version of the module should not be added here. The group is the same for all modules on the repository (com.hedera.hashgraph) and its definition can be found in the com.hedera.hashgraph.conventions plugin. For the version we use a global definition, too. The current version is defined in the gradle.properties file in the root folder of the project. The name of a module is simple created based on the folder name of the module.

Project sources sets

For a Java module all sources must be placed under src/main/java and src/main/resources while src/main/java must contain all the Java files that should be compiled. All other files must be placed under src/main/resources. Since all our Java based modules are full JPMS modules a module-info.java file must be placed directly under src/main/java.

All modules can have different types of tests. The com.hedera.hashgraph.javaConventions plugin provides direct support for unit test, integration tests, and end-to-end tests. Next to this the test fixtures functionality of Gradle is supported.

For the unit test set the src/test/java and src/test/resources folders must be used. Unit tests will be executed on the Java classpath and therefore no module-info.java is needed. This has the big benefit that unit tests can have the same base package as the module sources, access package private and use functionally that is not exported by the module-info.java under src/main/java.

For the integration test set the src/itest/java and src/itest/resources folders must be used. All integration tests will be executed on the module path to be as near to the real usage as possible. Based on that a module-info.java file is needed. The name for the module and the base package is based on the name of the source module plus the 'itest' suffix. Since the test fixtures JPMS module will only be used in tests it should be fully opened. This makes the content of the module-info.java much more readable since no individual opens statements need to be added.

For the end-to-end test set the src/eet/java and src/eet/resources folders must be used. TODO: Module path or class path?

A module can define test-fixtures that helps to create clean and readable unit tests. All Java sources for the test fixtures must be placed under src/testFixtures/java. Additional resources that should be shared for tests can be placed under src/testFixtures/resources. Like for the source set of a project the test fixtures sets are defined as full JPMS modules, too. Based on that a module-info.java file must be placed directly under src/testFixtures/java if at least one Java file is present. The name of the test fixtures module should be based on the name of the source module and add a testfixtures suffix. Since the test fixtures JPMS module will only be used in tests it should be fully opened. This makes the content of the module-info.java much more readable since no individual opens statements need to be added.

Next to tests JMH benchmarks can be part of a module to test the performance of critical components within the module. All benchmarks must be based on JMH and the src/jmh/java and src/jmh/resources folders must be used. TODO: Module path or class path?

Based on the given definitions a module folder in the project looks like this:

 foo-service/
 ├── src/main/java/
 │   ├── com.hedera.node.app.service.foo
 │   │   ├── FooService.java
 │   │   └── package-info.java
 │   └── module-info.java
 ├── src/main/resources/
 │   ├── com.hedera.node.app.service.foo
 │   │   └── some_data.json
 │   └── logging.properties
 ├── src/testFixtures/java/
 │   ├── com.hedera.node.app.service.foo.testfixtures
 │   │   └── FooServiceTestConfig.java
 │   └── module-info.java
 ├── src/test/java/
 │   └── com.hedera.node.app.service.foo
 │       └── FooServiceTest.java
 ├── src/itest/java/
 │   ├── com.hedera.node.app.service.foo.itest
 │   │   └── FooServiceITest.java
 │   └── module-info.java
 └── build.gradle.kts
lpetrovic05 commented 1 year ago

LGTM

hendrikebbers commented 1 year ago

Some issues of https://github.com/hashgraph/hedera-services/issues/7148 belongs to this epic

hendrikebbers commented 1 year ago

The doc needs an update