Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Architectural Design for Third-Party System Integration

Notes May 17 4

Signature Algorithm Abstraction

When integrating with external systems, API requests typically require authentication through AK/SK credentials with corresponding signature verification. A well-designed signature interface allows flexible support for multiple algorithms:

package com.integration.gateway.security;

import java.util.Map;

/**
 * Signature generation contract for external API authentication.
 * Supports multiple cryptographic approaches based on security requirements.
 */
public interface SignatureGenerator {

    /**
     * Specifies the cryptographic algorithm used for signature generation.
     *
     * @return the algorithm identifier
     */
    CryptoAlgorithm getAlgorithm();

    /**
     * Generates a signature for the provided parameter map.
     *
     * @param parameters request parameters to sign
     * @return hex-encoded signature string
     */
    String generate(Map<String, String> parameters);
}
package com.integration.gateway.security;

/**
 * Supported cryptographic algorithms for request signing.
 */
public enum CryptoAlgorithm {
    MD5("md5"),
    HMAC_SHA1("hmac-sha1"),
    HMAC_SHA256("hmac-sha256"),
    RSA_SHA256("rsa-sha256");

    private final String identifier;
    private final String description;

    CryptoAlgorithm(String identifier) {
        this.identifier = identifier;
        this.description = identifier;
    }

    public String getIdentifier() {
        return identifier;
    }
}

Access Token Lifecycle Management

Some external platforms issue time-limited access tokens that need global sharing across distributed services. These tokens require caching and refresh mechanisms:

package com.integration.gateway.auth;

/**
 * Manages the OAuth-style token acquisition workflow.
 * Encapsulates the code-to-token exchange process.
 *
 * @param <P> credentials required for authorization request
 * @param <C> authorization code response type
 * @param <T> access token response type
 */
public interface TokenAcquirer<P, C, T> {

    /**
     * Initiates authorization flow and retrieves temporary code.
     * Typically involves redirecting user to authorization server.
     *
     * @param requestParams platform-specific credentials
     * @return temporary authorization code
     */
    C requestAuthorizationCode(P requestParams);

    /**
     * Exchanges authorization code for access token.
     *
     * @param codeResponse response containing authorization code
     * @return access token with expiration details
     */
    T exchangeForAccessToken(C codeResponse);
}
package com.integration.gateway.auth;

import com.integration.gateway.auth.model.*;

/**
 * Base implementation for platform-specific token acquisition.
 * Concrete implementations handle HTTP communication details.
 */
public abstract class AbstractTokenAcquirer 
        implements TokenAcquirer<AuthRequest, AuthCodeResponse, TokenResponse> {

    @Override
    public AuthCodeResponse requestAuthorizationCode(AuthRequest params) {
        // Platform-specific implementation
        return null;
    }

    @Override
    public TokenResponse exchangeForAccessToken(AuthCodeResponse codeResponse) {
        // Platform-specific implementation
        return null;
    }
}
package com.integration.gateway.auth;

import com.integration.gateway.auth.model.*;

/**
 * Caching decorator for token management.
 * Enables distributed token sharing via Redis while maintaining
 * in-memory fallback for single-node deployments.
 */
public class CachedTokenAcquirer implements TokenAcquirer<AuthRequest, AuthCodeResponse, TokenResponse> {

    private final TokenAcquirer<AuthRequest, AuthCodeResponse, TokenResponse> delegate;

    public CachedTokenAcquirer(TokenAcquirer<AuthRequest, AuthCodeResponse, TokenResponse> delegate) {
        this.delegate = delegate;
    }

    @Override
    public AuthCodeResponse requestAuthorizationCode(AuthRequest params) {
        return delegate.requestAuthorizationCode(params);
    }

    @Override
    public TokenResponse exchangeForAccessToken(AuthCodeResponse codeResponse) {
        return delegate.exchangeForAccessToken(codeResponse);
    }
}

Message Queue Integration Pattern

When integrating via message queues, implementing a chain-of-responsibility pattern enables modular message processing:

package com.integration.messaging;

/**
 * Chain-of-responsibility handler for incoming messages.
 * Supports sequential processing through linked processors.
 */
public abstract class MessageProcessor {

    private MessageProcessor nextHandler;

    public MessageProcessor(MessageProcessor nextHandler) {
        this.nextHandler = nextHandler;
    }

    public MessageProcessor() {
    }

    /**
     * Processes a single message unit.
     *
     * @param payload message content
     * @param context processing context for state sharing
     * @return processing result
     */
    public abstract ProcessingResult handle(MessagePayload payload, ProcessingResult context);

    /**
     * Executes the processing chain.
     */
    public ProcessingResult execute(MessagePayload payload, ProcessingResult context) {
        handle(payload, context);
        if (nextHandler != null) {
            nextHandler.execute(payload, context);
        }
        return context;
    }
}

Message Filtering Strategy

Before processing, messages often require validation and filtering:

package com.integration.messaging.filter;

/**
 * Contract for message filtering logic.
 * Filters can be prioritized and grouped for organized execution.
 *
 * @param <M> message type to filter
 */
public interface MessageFilter<M> {

    /**
     * Evaluates whether the message passes this filter.
     *
     * @param message the message to evaluate
     * @return true if message should proceed, false to reject
     */
    boolean accept(M message);

    /**
     * Execution priority for filter ordering.
     * Lower values execute first.
     */
    int getPriority();

    /**
     * Logical grouping for filter categorization.
     */
    FilterCategory getCategory();
}

State Transition Management with Visitor Pattern

Handling message status transitions benefits from the visitor pattern cmobined with double dispatch, enabling clean state machine implementation:

package com.integration.messaging.state;

/**
 * Represents a database record's processing status.
 * Uses visitor pattern for status-dependent operations.
 */
public abstract class RecordStatus {

    private final int statusCode;

    protected RecordStatus(int statusCode) {
        this.statusCode = statusCode;
    }

    protected RecordStatus() {
        this.statusCode = 0;
    }

    /**
     * Accepts a visitor to perform status-specific operations.
     *
     * @param visitor the visitor implementation
     * @return result of the visit operation
     */
    public abstract ProcessingOutcome accept(PlatformVisitor visitor);

    protected StatusType resolveStatus() {
        return StatusType.fromCode(statusCode);
    }
}
package com.integration.messaging.state;

/**
 * Visitor interface for platform-specific status handling.
 * Implements double dispatch to route to appropriate visit method.
 */
public interface PlatformVisitor {

    /**
     * Determines if the given status indicates failure.
     */
    default boolean isFailureStatus(StatusType status) {
        return StatusType.ERROR == status 
            || StatusType.PARTIAL_FAILURE == status;
    }

    ProcessingOutcome visit(RecordStatus_Paid record);

    ProcessingOutcome visit(RecordStatus_Completed record);

    ProcessingOutcome visit(RecordStatus_Refunded record);
}

Supporting Infrastructure

Beyond core integration components, production systems require additional safeguards:

  • Rate Limiting: Prevent quota exhaustion and service degradation
  • Comprehensive Logging: Enable distributed tracing and debugging
  • Retry Mechanisms: Ensure eventual consistency for transient failures
  • Circuit Breakers: Isolate failing dependencies gracefully

These patterns form a cohesive architecture that supports extension without modification to existing code.

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.