Skip to main content

ArchUnit Analysis

This document describes how we use ArchUnit to enforce architectural constraints in your project.

Predefined Architecture Rules

Rule_1.5: Reactive flows should use aws async clients

Static Badge

This rule try to enforce the usage of AsyncClient variants of AWS SDK, for example the usage of kmsAsyncClient instead of kmsClient in reactive flows using the Mono.fromFuture method.

Rule_2.2: Domain classes should not be named with technology suffixes

Static Badge

This rule is intended to avoid the technical names usage in domain entities. We avoid UserDTO name instead of simply define as User.

you can use the gradle.properties file to set the forbidden suffixes, by default is:

arch.unit.forbiddenDomainSuffixes=dto,request,response

Note that plugin will treat dto suffix also like Dto and DTO

❌ No Compliant

package co.com.bancolombia.model.userrequest;

public class UserRequest {
private String name;
private String email;
}

✅ Compliant

package co.com.bancolombia.model.user;

public class User {
private String name;
private String email;
}

Rule_2.4: Domain classes should not be named with technology names

Static Badge

This rule is intended to avoid the technical names usage in domain entities, gateways and use cases. We avoid DynamoUserGateway name instead of simply define as UserGateway.

you can use the gradle.properties file to set the forbidden partial class names, by default is:

arch.unit.forbiddenDomainClassNames=rabbit,sqs,sns,ibm,dynamo,aws,mysql,postgres,redis,mongo,rsocket,r2dbc,http,kms,s3,graphql,kafka

Note that plugin will treat rabbit suffix also like Rabbit and RABBIT

❌ No Compliant

package co.com.bancolombia.model.rabbitmessage;

public class RabbitMessage {
private String contentType;
private byte[] content;
}

✅ Compliant

package co.com.bancolombia.model.message;

public class Message {
private String contentType;
private byte[] content;
}

Rule_2.7: Domain classes should not have fields named with technology names

Static Badge

The same purpose of Rule_2.4 but applied to domain model fields or use case fields.

you can use the gradle.properties file to set the forbidden field names, by default is the same that Rule_2.4, and can be override with the same property.

❌ No Compliant

package co.com.bancolombia.model.message;

public class Message {
private String rabbitContentType;
private byte[] content;
}

✅ Compliant

package co.com.bancolombia.model.message;

public class Message {
private String contentType;
private byte[] content;
}

Rule_2.5: UseCases should only have final attributes to avoid concurrency issues

Static Badge

This rule is designed to avoid the shared resources in concurrency, all dependencies of a Use Case should be immutable, and each context call should have their own resources.

❌ No compliant

package co.com.bancolombia.usecase.notifyusers;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class NotifyUsersUseCase {
private NotificationGateway notifier; // No Compliant
private User user; // No Compliant


public void notify(User user) {
this.user = user;
// some other business logic
this.sendMessage()
}

private void sendMessage() {
// send message througth class instance of user
notifier.send("notification", user.getEmail())
}
}

✅ Compliant

package co.com.bancolombia.usecase.notifyusers;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
public class NotifyUsersUseCase {
private final NotificationGateway notifier; // Compliant

public void notify(User user) {
// some other business logic
this.sendMessage(user)
}

private void sendMessage(User user) {
// send message througth input instance of user
notifier.send("notification", user.getEmail())
}
}

Rule_2.7: Beans classes should only have final attributes (injection by constructor) to avoid concurrency issues

Static Badge

This rule is designed to avoid the shared resources in concurrency, all dependencies of a Bean should be immutable, and each context call should have their own resources. We promote the dependencies injection through constructor over the @Value annotated fields injection.

❌ No Compliant


@Component
public class MyService {
@Value("${external-service.endpoint}")
private String endpoint; // No Compliant

public void someFunction(SomeObject domainObject) {
// ... logic that uses endpoint
}
}

✅ Compliant


@Component
public class MyService {
private final String endpoint; // Compliant

public MyService(@Value("${external-service.endpoint}") String endpoint) {
this.endpoint = endpoint;
}

public void someFunction(SomeObject domainObject) {
// ... logic that uses endpoint
}
}

Skip Rules

If you want to skip Arch Unit rules validation, you can use the gradle.properties file to add arch.unit.ski=true.

We recommend not to skip the rules, but if you need to do it, you can add the property to the gradle.properties file.

For example:

package=co.com.bancolombia
systemProp.version=3.17.22
reactive=true
lombok=true
metrics=true
language=java
org.gradle.parallel=true
systemProp.sonar.gradle.skipCompile=true
arch.unit.skip=true # Skip Arch Unit rules validation