-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes gb-17
- Loading branch information
mnhock
authored and
mnhock
committed
Jun 11, 2024
1 parent
686460b
commit 010d71a
Showing
1 changed file
with
242 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
## Taikai | ||
|
||
### 1. Introduction | ||
Taikai is an automated architecture testing tool for Java projects designed to maintain clean and consistent architecture. It enforces predefined and custom architectural constraints, ensuring code quality, maintainability, and adherence to best practices. | ||
|
||
### 2. Getting Started | ||
To use Taikai, include it as a dependency in your Maven pom.xml: | ||
|
||
```javaxml | ||
<dependency> | ||
<groupId>com.enofex</groupId> | ||
<artifactId>taikai</artifactId> | ||
<version>${taikai.version}</version> | ||
<scope>test</scope> | ||
</dependency> | ||
``` | ||
|
||
Architecture rules are defined using Taikai's fluent API, allowing developers to specify constraints on classes, methods, imports, naming conventions, and more. Taikai provides pre-defined configurations for common architectural patterns and best practices. | ||
|
||
### 3. Usage | ||
|
||
#### Test Configuration | ||
|
||
Test configuration involves specifying constraints related to testing frameworks and practices. Let's explore the rules defined in the provided source code: | ||
|
||
- **JUnit 5 Configuration**: Ensure that JUnit 5 test classes and methods are not annotated with `@Disabled`. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.test(test -> test | ||
.junit5(junit5 -> junit5 | ||
.classesShouldNotBeAnnotatedWithDisabled() | ||
.methodsShouldNotBeAnnotatedWithDisabled())) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
#### Java Configuration | ||
|
||
Java configuration involves defining constraints related to Java language features, coding standards, and architectural patterns. Let's examine the rules defined in the provided source code: | ||
|
||
- **No Usage of Deprecated APIs**: Ensure that deprecated APIs are not used in the codebase. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.java(java -> java | ||
.noUsageOfDeprecatedAPIs()) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
- **Classes Should Implement `hashCode` and `equals`**: Ensure that classes override the `hashCode` and `equals` methods for proper object comparison. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.java(java -> java | ||
.classesShouldImplementHashCodeAndEquals()) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
- **Methods Should Not Throw Generic Exception**: Ensure that methods do not throw generic exceptions and use specific exception types instead. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.java(java -> java | ||
.methodsShouldNotThrowGenericException()) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
- **Utility Classes Should Be Final and Have Private Constructor**: Ensure that utility classes are declared as `final` and have private constructors to prevent instantiation. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.java(java -> java | ||
.utilityClassesShouldBeFinalAndHavePrivateConstructor()) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
- **Imports Configuration**: Ensure that there are no cyclic dependencies in imports and disallow specific imports. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.java(java -> java | ||
.imports(imports -> imports | ||
.shouldHaveNoCycles() | ||
.shouldNotImport("..shaded..") | ||
.shouldNotImport("..lombok.."))) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
- **Naming Configuration**: Define naming conventions for classes, constants, and interfaces. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.java(java -> java | ||
.naming(naming -> naming | ||
.classesShouldNotMatch(".*Impl") | ||
.constantsShouldFollowConvention() | ||
.interfacesShouldNotHavePrefixI()))) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
#### Spring Configuration | ||
|
||
Spring configuration involves defining constraints specific to Spring Framework usage. Let's incorporate the Spring-related rules defined in the provided source code: | ||
|
||
- **No Autowired Fields Configuration**: Ensure that fields are not annotated with `@Autowired` and constructor injection is preferred. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.spring(spring -> spring | ||
.noAutowiredFields()) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
- **Spring Boot Configuration**: Ensure that the main application class annotated with `@SpringBootApplication` is in the default package. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.spring(spring -> spring | ||
.boot(boot -> boot | ||
.springBootApplicationShouldBeIn("com.company.yourproject"))) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
- **Configurations Configuration**: Ensure that configuration classes end with "Configuration" or match a specific regex pattern. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.spring(spring -> spring | ||
.configurations(configuration -> configuration | ||
.namesShouldEndWithConfiguration() | ||
.namesShouldMatch("regex"))) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
- **Controllers Configuration**: Ensure that controller classes end with "Controller" or match a specific regex pattern, are annotated with `@RestController`, do not depend on other controllers, and are package-private. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.spring(spring -> spring | ||
.controllers(controllers -> controllers | ||
.shouldBeAnnotatedWithRestController() | ||
.namesShouldEndWithController() | ||
.namesShouldMatch("regex") | ||
.shouldNotDependOnOtherControllers() | ||
.shouldBePackagePrivate())) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
- **Services Configuration**: Ensure that service classes end with "Service" or match a specific regex pattern and are annotated with `@Service`. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.spring(spring -> spring | ||
.services(services -> services | ||
.shouldBeAnnotatedWithService() | ||
.namesShouldMatch("regex") | ||
.namesShouldEndWithService())) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
- **Repositories Configuration**: Ensure that repository classes end with "Repository" or match a specific regex pattern and are annotated with `@Repository`. | ||
|
||
```java | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.spring(spring -> spring | ||
.repositories(repositories -> repositories | ||
.shouldBeAnnotatedWithRepository() | ||
.namesShouldMatch("regex") | ||
.namesShouldEndWithRepository())) | ||
.build() | ||
.check(); | ||
``` | ||
|
||
#### Examples | ||
|
||
Below are some examples demonstrating the usage of Taikai to define and enforce architectural rules in Java projects, including Spring-specific configurations: | ||
|
||
```java | ||
class ArchitectureTest { | ||
|
||
@Test | ||
void shouldFulfilConstrains() { | ||
Taikai.builder() | ||
.namespace("com.company.yourproject") | ||
.test(test -> test | ||
.junit5(junit5 -> junit5 | ||
.classesShouldNotBeAnnotatedWithDisabled() | ||
.methodsShouldNotBeAnnotatedWithDisabled())) | ||
.java(java -> java | ||
.noUsageOfDeprecatedAPIs() | ||
.classesShouldImplementHashCodeAndEquals() | ||
.methodsShouldNotThrowGenericException() | ||
.utilityClassesShouldBeFinalAndHavePrivateConstructor()) | ||
.spring(spring -> spring | ||
.repositories(repositories -> repositories | ||
.namesShouldEndWithRepository() | ||
.shouldBeAnnotatedWithRepository()) | ||
.services(services -> services | ||
.namesShouldEndWithService() | ||
.shouldBeAnnotatedWithService()) | ||
.boot(boot -> boot | ||
.shouldBeAnnotatedWithSpringBootApplication()) | ||
.noAutowiredFields() | ||
.configurations(configuration -> configuration | ||
.namesShouldEndWithConfiguration() | ||
.namesShouldMatch("regex")) | ||
.controllers(controllers -> controllers | ||
.shouldBeAnnotatedWithRestController() | ||
.namesShouldEndWithController() | ||
.namesShouldMatch("regex") | ||
.shouldNotDependOnOtherControllers() | ||
.shouldBePackagePrivate())) | ||
.build() | ||
.check(); | ||
} | ||
} | ||
``` |