À l'époque de sa création, Circit fonctionnait avec une petite équipe de moins de 10 employés, jonglant avec de multiples produits et faisant face à des ressources et une expertise limitées dans le domaine émergent de l'Open Banking. Avec l'avènement de la PSD2 et la poussée du secteur vers la standardisation, une opportunité est apparue de révolutionner la manière dont les auditeurs accèdent aux données bancaires. Cependant, la complexité de la tâche semblait intimidante, en particulier compte tenu de la position naissante de Circit sur le marché.

Reconnaissant la nécessité d'adopter une approche structurée, nous avons entrepris un voyage familier à de nombreux produits logiciels : définition d'un modèle de domaine, établissement d'une terminologie omniprésente, création d'un modèle canoniqueet élaboration d'un ensemble d'abstractions. Ces étapes fondamentales ont fourni une feuille de route pour le développement futur, mais ont également mis en évidence le défi que représente la prise de décisions concrètes avec des ressources et une expertise limitées.

Pour surmonter cet obstacle, Circit a pris la décision stratégique de s'appuyer sur les cadres existants, en se concentrant particulièrement sur le marché britannique où l'entreprise avait ses principales activités. En s'alignant sur le cadre Open Banking du Royaume-Uni, Circit a été en mesure d'accélérer son processus de développement et de fournir des solutions adaptées aux intégrations bancaires locales.

Cependant, lorsque l'entreprise s'est développée sur de nouveaux marchés, tels que l'Europe et au-delà, elle a été confrontée à une nouvelle série de défis. De nombreuses demandes d'intégration provenaient de banques non basées au Royaume-Uni, ce qui rendait le modèle de domaine et la terminologie existants obsolètes. Une fois de plus, Circit s'est retrouvée à la croisée des chemins, devant revoir son approche pour répondre aux diverses exigences du marché.

Conscients de l'impossibilité de construire des intégrations sur mesure pour chaque nouveau marché, nous avons adopté une stratégie fondée sur les principes de la division et de l'exploitation. Au fur et à mesure de l'émergence de nouveaux cadres bancaires tels que XS2A, STET et EBICS, nous les avons transformés en ce qu'ils ont appelé des "protocoles", c'est-à-dire des sous-domaines. Ces protocoles englobent les fonctionnalités de base de chaque cadre, en fournissant une interface normalisée pour l'intégration.

Cependant, le défi réside dans les diverses options de mise en œuvre offertes par les banques au sein de chaque cadre. Par exemple, alors que le protocole STET fournit un ensemble de caractéristiques standardisées, les banques individuelles peuvent le mettre en œuvre avec des approches d'autorisation client différentes, telles que Basic, JWT ou TLS. Nous avons reconnu la nécessité d'une solution flexible capable de s'adapter à ces variations sans sacrifier l'efficacité ou l'évolutivité.

Pour relever ce défi, nous avons exploité la puissance de la virtualisation et de l'abstraction au sein de l'environnement C# et de l'abstraction. En faisant abstraction des détails de mise en œuvre spécifiques et en créant des représentations virtuelles des protocoles bancaires, nous avons développé une architecture modulaire et extensible.

// Define an abstract base class for banking protocols
public abstract class BankingProtocol
{
    // Define common methods and properties for all protocols
    public abstract void HandleAuthorization(string authorizationMethod);
}

// Define concrete implementations for specific banking protocols
public class STETProtocol : BankingProtocol
{
    public override void HandleAuthorization(string authorizationMethod)
    {
        // Implement STET-specific authorization handling logic
        Console.WriteLine($"Handling authorization for STET protocol using {authorizationMethod} method.");
    }
}

public class XS2AProtocol : BankingProtocol
{
    public override void HandleAuthorization(string authorizationMethod)
    {
        // Implement XS2A-specific authorization handling logic
        Console.WriteLine($"Handling authorization for XS2A protocol using {authorizationMethod} method.");

  • Nous définissons une classe de base abstraite BankingProtocol avec une méthode HandleAuthorization, qui représente le comportement commun à tous les protocoles bancaires.
  • Les implémentations concrètes(STETProtocol et XS2AProtocol) héritent de la classe de base et fournissent une logique spécifique au protocole pour la gestion des autorisations.
  • Nous utilisons une classe d'usine ProtocolFactory pour créer des instances de protocoles spécifiques sur la base du nom de protocole fourni.
  • Dans la méthode Main, nous montrons comment utiliser la fabrique pour créer une instance de protocole et invoquer la méthode HandleAuthorization.

.

En adoptant une approche de conception qui privilégie l'isolation par fournisseur d'API, nous avons effectivement minimisé la redondance et rationalisé les efforts de développement. Bien qu'elle ait été confrontée à des protocoles presque identiques chez les différents fournisseurs, l'entreprise a mis en œuvre un système qui garantissait que chaque fournisseur fonctionnait de manière indépendante. Cette approche, ancrée dans les principes d'abstraction procédurale et conceptuelle, nous a permis de réduire considérablement les frais généraux de développement. Plutôt que de dupliquer le code pour des protocoles similaires, nous avons exploité la puissance de l'injection de dépendances et de la configuration. Ce faisant, l'entreprise a éliminé les tâches de codage répétitives, permettant aux développeurs de se concentrer sur la configuration des dépendances plutôt que sur la réécriture du code. Cette stratégie proactive a non seulement permis d'atténuer le risque de duplication du code, mais aussi d'améliorer la maintenabilité et l'évolutivité.

public abstract class ApiProvider
{
    public abstract void HandleRequest(string requestData);
}

// Define concrete implementations for API providers corresponding to different protocols
public class STETApiProvider : ApiProvider
{
    public override void HandleRequest(string requestData)
    {
        // Implement logic specific to handling requests for the STET protocol
        Console.WriteLine("Handling request using STET protocol: " + requestData);
    }
}

public class XS2AApiProvider : ApiProvider
{
    public override void HandleRequest(string requestData)
    {
        // Implement logic specific to handling requests for the XS2A protocol
        Console.WriteLine("Handling request using XS2A protocol: " + requestData);
    }
}

Dans cet exemple de code :

  • Nous définissons une classe de base abstraite ApiProvider qui représente le comportement commun à tous les fournisseurs d'API.
  • Les implémentations concrètes (STETApiProvider et XS2AApiProvider) héritent de la classe de base et fournissent une logique spécifique au protocole pour le traitement des demandes.
  • Nous utilisons une classe d'usine ApiProviderFactory pour créer des instances de fournisseurs d'API sur la base du nom de protocole fourni.
  • Dans la méthode Main, nous montrons comment utiliser la fabrique pour créer un fournisseur d'API basé sur le nom de protocole spécifié et simuler le traitement d'une requête à l'aide du fournisseur créé.

Les abstractions fondamentales de bas niveau et de protocole étant en place, nous pouvons maintenant établir des abstractions au niveau du domaine qui encapsulent chaque fournisseur d'API.

using System;

// Define an abstract base class for API providers
public abstract class ApiProvider
{
    public abstract void HandleRequest(string requestData);
}

// Define concrete implementations for API providers corresponding to different protocols
public class STETApiProvider : ApiProvider
{
    public override void HandleRequest(string requestData)
    {
        // Implement logic specific to handling requests for the STET protocol
        Console.WriteLine("Handling request using STET protocol: " + requestData);
    }
}

public class XS2AApiProvider : ApiProvider
{
    public override void HandleRequest(string requestData)
    {
        // Implement logic specific to handling requests for the XS2A protocol
        Console.WriteLine("Handling request using XS2A protocol: " + requestData);
    }
}

Dans cet exemple actualisé :

  • Nous introduisons une BankApi qui représente une abstraction au niveau du domaine pour un fournisseur d'API. Cette classe encapsule le fournisseur d'API de bas niveau spécifique au protocole sous-jacent.
  • La BankApi prend un nom de protocole en paramètre lors de l'instanciation et crée en interne le fournisseur d'API de bas niveau correspondant à l'aide de ApiProviderFactory.
  • Les HandleRequest de la méthode BankApi délègue la gestion des demandes au fournisseur d'API sous-jacent.

L'utilisation de ces abstractions au niveau du domaine nous permet de mettre en œuvre de manière transparente des intégrations supplémentaires sans interférer avec les protocoles existants, simplement en mettant en œuvre de nouvelles abstractions au niveau du domaine.

Nous vous avons présenté le premier modèle que nous utilisons, appelé Abstract Factory.

public static class ApiProviderFactory
{
    public static ApiProvider CreateApiProvider(string protocolName)
    {
        // Logic to determine which API provider to instantiate based on the protocol name
        switch (protocolName.ToUpper())
        {
            case "STET":
                return new STETApiProvider();
            case "XS2A":
                return new XS2AApiProvider();
            default:
                throw new ArgumentException("Unsupported protocol name.");
        }
    }
}

// Define a domain-level abstraction representing an API provider
public class BankApi
{
    private readonly ApiProvider _apiProvider;

    public BankApi(string protocolName)
    {
        // Create an API provider based on the specified protocol name
        _apiProvider = ApiProviderFactory.CreateApiProvider(protocolName);
    }

    // Method to handle a request using the underlying API provider
    public void HandleRequest(string requestData)
    {
        _apiProvider.HandleRequest(requestData);
    }
}

Dans cet exemple :

  • Nous introduisons une BankApi qui représente une abstraction au niveau du domaine pour un fournisseur d'API. Cette classe encapsule le fournisseur d'API de bas niveau spécifique au protocole sous-jacent.
  • La BankApi prend un nom de protocole en paramètre lors de l'instanciation et crée en interne le fournisseur d'API de bas niveau correspondant à l'aide de la classe ApiProviderFactory.
  • Les HandleRequest de la méthode BankApi délègue la gestion des demandes au fournisseur d'API sous-jacent.

Les processus d'intégration comprennent deux éléments essentiels : L'autorisation et la récupération des données. Ces deux éléments sont abstraits d'une manière similaire à ce qui a été décrit précédemment, en commençant par l'autorisation.

Au départ, notre approche était centrée sur un flux d'authentification redirigé. Cependant, au fur et à mesure que nos activités se sont développées, nous avons exploré une myriade de méthodes alternatives, y compris des approches intégrées et découplées. En outre, une grande partie des banques mettent en œuvre des flux d'autorisation multiples, ce qui ajoute des couches de complexité au paysage. Par conséquent, chaque fournisseur d'API et chaque protocole peuvent être conceptualisés comme une collection d'abstractions. Cela nous amène directement à notre prochain sujet de discussion : le modèle de stratégie.

// Define an abstract base class for Authorization strategies
public abstract class AuthorizationStrategy
{
    public abstract void Authorize();
}

// Concrete implementations for Authorization strategies
public class RedirectAuthorization : AuthorizationStrategy
{
    public override void Authorize()
    {
        Console.WriteLine("Performing redirect authorization...");
    }
}

public class EmbeddedAuthorization : AuthorizationStrategy
{
    public override void Authorize()
    {
        Console.WriteLine("Performing embedded authorization...");
    }
}

// Factory class to create instances of Authorization strategies based on the approach
public static class AuthorizationStrategyFactory
{
    public static AuthorizationStrategy CreateAuthorizationStrategy(string approach)
    {
        switch (approach.ToUpper())
        {
            case "REDIRECT":
                return new RedirectAuthorization();
            case "EMBEDDED":
                return new EmbeddedAuthorization();
            // Add more cases for other authorization approaches as needed
            default:
                throw new ArgumentException("Unsupported authorization approach.");
        }
    }
}

// Domain-level abstraction representing authorization for a specific API provider
public class AuthorizationProcess
{
    private readonly AuthorizationStrategy _authorizationStrategy;

    public AuthorizationProcess(string approach)
    {
        _authorizationStrategy = AuthorizationStrategyFactory.CreateAuthorizationStrategy(approach);
    }

    public void PerformAuthorization()
    {
        _authorizationStrategy.Authorize();
    }
}

Dans cet exemple :

  • Nous introduisons un nouvel ensemble d'abstractions pour les processus d'autorisation, en suivant la même structure que les fournisseurs d'API.
  • La stratégie d'autorisation AuthorizationStrategyFactory crée des instances de stratégies d'autorisation concrètes basées sur l'approche fournie.
  • Le processus d'autorisation Processus d'autorisation encapsule la stratégie d'autorisation sélectionnée et fournit une méthode pour exécuter le processus d'autorisation.
  • La méthode Main montre comment utiliser le processus d'autorisation, de la même manière que nous avons utilisé le fournisseur d'API dans l'exemple précédent.

Avec un pivot stratégique, nous nous sommes embarqués dans une quête pour encapsuler la myriade d'approches d'autorisation qui s'offraient à eux. Du flux de redirection familier aux complexités de l'autorisation intégrée et découplée, chaque méthode a trouvé sa représentation dans l'architecture évolutive de Circit.

Cette transformation s'est appuyée sur l'établissement d'une abstraction fondamentale : l'AuthorizationStrategy. Cette entité abstraite a servi de modèle pour la myriade d'approches d'autorisation que nous allions rencontrer. Des implémentations concrètes telles que RedirectAuthorization et EmbeddedAuthorization ont donné vie à ces abstractions, chacune représentant une facette unique du paysage de l'autorisation.

Mais l'abstraction ne suffisait pas. Chez Circit, nous avions besoin d'un mécanisme permettant d'instancier ces stratégies de manière transparente, en s'adaptant aux exigences nuancées de chaque intégration. C'est ainsi qu'est née l'AuthorizationStrategyFactory. Avec cette fabrique à leur disposition, nous pouvions conjurer la stratégie d'autorisation précise nécessaire pour tout scénario donné, qu'il s'agisse de la simplicité d'une redirection ou de la sophistication d'un flux intégré.

Alors que la poussière retombait et que le système de Circit mûrissait, l'AuthorizationProcess est apparu comme le pivot de leur cadre d'autorisation. Cette abstraction au niveau du domaine a servi de conduit entre la stratégie et l'exécution, orchestrant les complexités de l'autorisation avec finesse et précision.

Armés du modèle stratégique et fortifiés par des abstractions au niveau du domaine, nous étions prêts à affronter le paysage en constante évolution de l'intégration bancaire. À chaque nouveau défi, ils ont adopté la polyvalence et l'adaptabilité offertes par leur conception stratégique, allant de l'avant vers un avenir riche en possibilités et en innovations.

En ce qui concerne le processus de récupération des données, nous nous concentrons sur trois opérations principales : la récupération de tous les comptes, la récupération des soldes pour chaque compte et l'obtention des détails des transactions. Cela résume la complexité de notre mécanisme de recherche de données.

Pour rationaliser ce processus, nous utilisons la méthode des modèles. Ce choix stratégique nous permet de construire un pipeline cohérent à l'aide d'abstractions, facilitant ainsi la récupération transparente des données.

// Abstract class representing the data retrieval process
public abstract class DataRetrievalProcess
{
    // Template method defining the data retrieval pipeline
    public void RetrieveData()
    {
        // Step 1: Fetch all accounts
        List<Account> accounts = FetchAllAccounts();

        // Step 2: Retrieve balances for each account
        foreach (var account in accounts)
        {
            decimal balance = RetrieveBalance(account);
            Console.WriteLine($"Balance for Account {account.AccountNumber}: {balance}");
        }

        // Step 3: Retrieve transaction details for each account
        foreach (var account in accounts)
        {
            List<Transaction> transactions = RetrieveTransactions(account);
            Console.WriteLine($"Transactions for Account {account.AccountNumber}:");
            foreach (var transaction in transactions)
            {
                Console.WriteLine(transaction);
            }
        }
    }

    // Abstract methods representing steps in the data retrieval process
    protected abstract List<Account> FetchAllAccounts();
    protected abstract decimal RetrieveBalance(Account account);
    protected abstract List<Transaction> RetrieveTransactions(Account account);
}

// Sample Account class
public class Account
{
    public string AccountNumber { get; set; }
    // Other properties
}

// Sample Transaction class
public class Transaction
{
    // Transaction properties
}

// Concrete implementation of the data retrieval process
public class ConcreteDataRetrievalProcess : DataRetrievalProcess
{
    // Mock data for demonstration purposes
    protected override List<Account> FetchAllAccounts()
    {
        return new List<Account>
        {
            new Account { AccountNumber = "123456789" },
            new Account { AccountNumber = "987654321" }
        };
    }

    protected override decimal RetrieveBalance(Account account)
    {
        // Mock balance retrieval logic
        return 1000.00m; // Dummy balance value
    }

    protected override List<Transaction> RetrieveTransactions(Account account)
    {
        // Mock transaction retrieval logic
        return new List<Transaction>
        {
            new Transaction { /* Transaction details */ },
            new Transaction { /* Transaction details */ }
        };
    }
}

Dans cet exemple de code :

  • Nous avons créé une classe abstraite DataRetrievalProcess pour encapsuler le processus de récupération des données. Elle fournit une méthode modèle RetrieveData() qui définit la séquence englobant la recherche des comptes, l'obtention des soldes et l'extraction des transactions.
  • Les sous-classes concrètes proposent des implémentations pour FetchAllAccounts(), RetrieveBalance()et RetrieveTransactions()et adaptent le comportement à des sources de données distinctes.
  • Une implémentation concrète, ConcreteDataRetrievalProcessfournit une logique de recherche de données fictive, illustrant l'application pratique du modèle.
  • Dans la méthode Main, nous instançons ConcreteDataRetrievalProcess et exécutons le processus de récupération des données.

Comme nous l'avons vu précédemment, la gestion de centaines d'intégrations implique la prise en compte de divers contrats de données et d'interprétations sur différentes plates-formes. Chaque intégration apporte ses propres nuances, telles que des interprétations différentes des types de soldes. Cependant, au milieu de cette complexité, nous avons reconnu l'opportunité d'exploiter un modèle de données canonique, fournissant une représentation unifiée des points de données essentiels.

Ce modèle de données canonique nous sert de base, offrant un format standardisé pour le traitement des données à travers les intégrations. Il nous permet d'identifier et de hiérarchiser les données utiles en fonction des besoins du client, en assurant la cohérence de nos opérations.

Pour combler le fossé entre le modèle de données canonique et les exigences uniques de chaque fournisseur d'API, nous avons introduit un nouveau modèle : le modèle d'adaptateur. Au niveau du fournisseur d'API, les adaptateurs sont chargés de traduire les données du modèle canonique dans le format spécifique requis par chaque intégration. Cette délégation de responsabilité garantit une unification transparente des données tout en tenant compte des particularités des différentes plateformes.

// Canonical data model representing essential data points
public class CanonicalDataModel
{
    public string AccountNumber { get; set; }
    public decimal Balance { get; set; }
    // Other properties relevant to the canonical model
}

// Interface for API provider adapters
public interface IApiProviderAdapter
{
    void ConvertAndSendData(CanonicalDataModel data);
}

// Concrete adapter for API provider A
public class ApiProviderAAdapter : IApiProviderAdapter
{
    public void ConvertAndSendData(CanonicalDataModel data)
    {
        // Convert canonical data to format specific to API provider A
        Console.WriteLine("Converting data to format specific to API provider A:");
        Console.WriteLine($"Account: {data.AccountNumber}, Balance: {data.Balance}");
        // Send data to API provider A
        Console.WriteLine("Sending data to API provider A...");
    }
}

// Concrete adapter for API provider B
public class ApiProviderBAdapter : IApiProviderAdapter
{
    public void ConvertAndSendData(CanonicalDataModel data)
    {
        // Convert canonical data to format specific to API provider B
        Console.WriteLine("Converting data to format specific to API provider B:");
        Console.WriteLine($"Account: {data.AccountNumber}, Balance: {data.Balance}");
        // Send data to API provider B
        Console.WriteLine("Sending data to API provider B...");
    }
}

// Client class responsible for orchestrating data unification and sending to API providers
public class DataUnificationClient
{
    private readonly List<IApiProviderAdapter> _adapters;

    public DataUnificationClient()
    {
        // Initialize adapters for different API providers
        _adapters = new List<IApiProviderAdapter>
        {
            new ApiProviderAAdapter(),
            new ApiProviderBAdapter()
            // Add more adapters for other API providers as needed
        };
    }

    // Method to unify data and send to all API providers
    public void UnifyAndSendData(CanonicalDataModel data)
    {
        foreach (var adapter in _adapters)
        {
            adapter.ConvertAndSendData(data);
        }
    }
}

Dans cet exemple :

  • Nous définissons un modèle de données canonique (CanonicalDataModel) représentant les points de données essentiels partagés par différents fournisseurs d'API.
  • Nous créons une interface IApiProviderAdapter représentant le modèle d'adaptateur, avec une méthode ConvertAndSendData pour convertir les données canoniques et les envoyer au fournisseur d'API concerné.
  • Classes d'adaptateurs concrets (ApiProviderAAdapter et ApiProviderBAdapter) mettent en œuvre le modèle IApiProviderAdapter pour convertir les données canoniques dans des formats spécifiques aux fournisseurs d'API A et B, respectivement.
  • Le client DataUnificationClient orchestre le processus d'unification des données en parcourant une liste d'adaptateurs et en envoyant les données converties à tous les fournisseurs d'API.
  • Dans la méthode Main, nous créons une instance du modèle de données canonique, nous instancions le module DataUnificationClientet unifions les données, démontrant ainsi le modèle d'adaptateur en action pour l'unification des données à travers plusieurs fournisseurs d'API.

En conclusion, le parcours de Circit souligne le pouvoir de transformation de l'adoption de modèles de conception stratégiques pour faire face aux complexités de l'intégration des API. Commençant avec des ressources et une expertise limitées, Circit a capitalisé sur des changements réglementaires tels que PSD2 pour redéfinir l'accès des auditeurs aux données. En adoptant des modèles tels que le modèle de stratégie, l'entreprise a su faire face à l'expansion du marché et à l'évolution des demandes d'intégration avec agilité.

L'adoption d'un modèle de données canonique a facilité la représentation transparente des données à travers les intégrations, tandis que les adaptateurs ont comblé le fossé entre le modèle canonique et les exigences spécifiques de l'API. En outre, l'arsenal d'intégration de Circit a été renforcé par des modèles supplémentaires tels que les mécanismes de réessai et le traitement parallèle, garantissant la fiabilité et l'optimisation des performances.

En substance, la réussite de Circit illustre la manière dont l'adoption de modèles de conception stratégiques permet aux organisations de s'adapter aux changements réglementaires, de répondre à l'évolution des demandes du marché et d'atteindre de nouveaux niveaux d'efficacité et d'innovation en matière d'intégration des API.

En conclusion de notre discussion sur les solutions innovantes de Circit en matière d'intégration d'API et de gestion de données, nous vous invitons à explorer quelques questions intéressantes :

  1. L'API de consentement en détails : Comment l'API de consentement de Circit assure-t-elle la confidentialité des utilisateurs tout en permettant un accès transparent aux données critiques à des fins d'audit ?
  2. L'article 10 en détail : Quel éclairage l'article 10 apporte-t-il à la documentation de Circit, et comment contribue-t-il à notre compréhension de la conformité réglementaire et des protocoles de traitement des données ?
  3. MATLS.
  4. Les flux d'authentification démystifiés : Vous souhaitez approfondir les flux d'authentification de Circit, tels que la redirection, l'authentification découplée et l'authentification intégrée? Découvrez le fonctionnement de chaque flux et leurs implications pour la sécurité et l'expérience utilisateur.
  5. L'enregistrement dynamique des clients : Curieux d'en savoir plus sur l'enregistrement dynamique des clients et son rôle dans OAuth 2.0? Découvrez son importance dans l'écosystème Circit et comment il facilite l'intégration transparente avec divers fournisseurs d'API.

Nous sommes impatients d'approfondir ces sujets dans nos prochaines publications, afin de vous fournir une compréhension complète de l'ensemble des technologies et des stratégies réglementaires de Circit. Restez à l'écoute pour plus d'informations et de découvertes !

Télécharger le pdf
Demander une démo

Découvrez ce que Circit peut faire pour votre entreprise