Skip to content

Architecture Rules

JuanC Galvis edited this page Jun 2, 2023 · 2 revisions

Predefined Architecture Rules

Reactive Programming

Rule_1.5: Reactive flows should use aws async clients

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.

Clean Architecture

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

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

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

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.

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

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

Concurrency Safe Code

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

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

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
    }
}