Separate Business and Control Logic

2025.05.15 · 3 minute read

The core logic is to separate the parts of the program that handle business and domain knowledge (domain or business logic) from the parts responsible for process control, coordination, and decision-making (control logic), thereby improving code maintainability, testability, and reusability.

In DDD, business logic corresponds to domain logic, and control logic corresponds to the “application layer” or “service layer.”

Business Logic

  • Content: Business rules, data validation, calculation formulas
  • Responsibility: Ensure system behavior complies with business requirements and specifications
  • Examples:
    • Calculating the total price of an order
    • Determining user permissions
    • Business state transitions

Control Logic

  • Content: Process control, call sequence, conditional judgment
  • Responsibility: Organize and manage the execution of business logic to ensure correct system flow
  • Examples:
    • Handling state transitions in multi-step business processes
    • Controlling transaction start and commit

Examples

Using Python

Conventional Approach

class UserService:
    def __init__(self):
        self.users = []

    def register_user(self, username, password):
        # 1. validate username
        if username in self.users:
            return {"success": False, "message": "Username already exists"}

        # 2. validate password length
        if len(password) < 6:
            return "Password too short"

        # 3. store user
        self.users.append(username)
        return {"success": True, "message": "User registered successfully"}

Separated Approach

class UserRepository:
    def __init__(self):
        self.users = {}

    def exists(self, username):
        return username in self.users

    def save(self, user):
        self.users[user["username"]] = user

# Domain Logic
class UserDomainService:
    def __init__(self, user_repository):
        self.user_repository = user_repository

    def is_username_available(self, username):
        # Check if the username is already taken
        return self.user_repository.exists(username)

    def validate_password(self, password):
        # Validate the password according to your criteria
        return len(password) >= 6

    def create_user(self, username, password):
        user = {"username": username, "password": password}
        self.user_repository.save(user)
        return user

# Application Logic
class UserApplicationService:
    def __init__(self, user_domain_service):
        self.user_domain_service = user_domain_service

    def register_user(self, username, password):
        # 1. Check if the username is available
        if self.user_domain_service.is_username_available(username):
            return {"success": False, "message": "Username already taken."}

        # 2. Validate the password
        if not self.user_domain_service.validate_password(password):
            return {"success": False, "message": "Password does not meet criteria."}

        # 3. Create the user
        user = self.user_domain_service.create_user(username, password)
        return {"success": True, "user": user}

repo = UserRepository()
domain_service = UserDomainService(repo)
app_service = UserApplicationService(domain_service)

print(app_service.register_user("john_doe", "password123"))  # Should succeed
print(
    app_service.register_user("john_doe", "pass")
)  # Should fail due to password criteria
print(
    app_service.register_user("john_doe", "newpassword")
)  # Should fail due to username already taken
  • UserDomainService focuses only on business rules (whether the username exists, password validation, user creation).
  • UserApplicationService is responsible for process control (call sequence, return results).

Using Java

Conventional Approach

import java.util.HashSet;
import java.util.Set;

public class UserService {
    private Set<String> users = new HashSet<>();

    public Result register(String username, String password) {
        // 1. Check if the username is available
        if (users.contains(username)) {
            return new Result(false, "Username is already in use");
        }
        // 2. Validate the password
        if (password.length() < 6) {
            return new Result(false, "Password must be at least 6 characters");
        }

        // 3. Create the user
        users.add(username);

        return new Result(true, "success");
    }

    public static class Result {
        private boolean success;
        private String message;

        public Result(boolean success, String message) {
            this.success = success;
            this.message = message;
        }
        public boolean isSuccess() {return success;}
        public String getMessage() {return message;}
    }
}

Separated Approach

import java.util.HashMap;
import java.util.Map;

// Domain Logic
class UserDomainService {
    private UserRepository userRepository;

    public UserDomainService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public boolean isUsernameTaken(String username) {
        return userRepository.exists(username);
    }

    public boolean validatePassword(String password){
        return password != null && password.length() >= 6;
    }

    public void createUser(String username, String password) {
        User user = new User(username, password);
        userRepository.save(user);
    }
}

// Persistence Layer
class UserRepository{
    private Map<String, User> users = new HashMap<>();

    public boolean exists(String username){
        return users.containsKey(username);
    }

    public void save(User user){
        users.put(user.getUsername(), user);
    }
}

// User Entity
class User {
    private String username;
    private String password;

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    public String getUsername() {
        return username;
    }
}

class Result {
    private boolean success;
    private String message;

    public Result(boolean success, String message) {
        this.success = success;
        this.message = message;
    }
    public boolean isSuccess() {return success;}
    public String getMessage() {return message;}
}

// Application Logic
class UserApplicationService {
    private UserDomainService userDomainService;

    public UserApplicationService(UserDomainService userDomainService) {
        this.userDomainService = userDomainService;
    }

    public Result register(String username, String password) {
        if(userDomainService.isUsernameTaken(username))
            return new Result(false, "Username taken");

        if(!userDomainService.validatePassword(password))
            return new Result(false, "Invalid password");

        userDomainService.createUser(username, password);

        return new Result(true, "success");
    }
}

public class scratch_46 {
    public static void main(String[] args) {
        UserRepository userRepository = new UserRepository();
        UserDomainService userDomainService = new UserDomainService(userRepository);
        UserApplicationService userApplicationService = new UserApplicationService(userDomainService);

        Result r1 = userApplicationService.register("alice", "12345");
        System.out.println(r1.getMessage());
        Result r2 = userApplicationService.register("alice", "123456");
        System.out.println(r2.getMessage());
        Result r3 = userApplicationService.register("alice", "1234567");
        System.out.println(r3.getMessage());
    }
}
  • UserDomainService is responsible for business rules (whether the username exists, password validation, user creation).
  • UserApplicationService is responsible for controlling the process (call sequence, return results).
  • UserRepository simulates data storage.
  • User is the domain entity.

Tips and Suggestions

  1. Start by designing simple methods.
  2. Move simple methods to the control logic layer.
  3. Separate out the business logic.

Thank you for reading! Your support is appreciated.

If you enjoyed this, consider buying me a coffee. ☕️