I. Introduction aux concepts SCA et SOA

SCA (Service Component Architecture) est un concept qui définit un ensemble de stratégies pour la mise en oeuvre d'applications à base de composants afin d'adresser les problématiques SOA (Service Oriented Architecture). Un des points fondamentaux dans SCA est qu'un service de niveau N se construit par orchestration (ou combinaison) de services de niveau N-1 ou N.

Dans une application qui suit SCA on distingue 2 éléments :

  • les composants SOA qui fournissent et consomment des services,
  • l'assemblage des composants SOA qui donne lieu à une application métier de haut niveau.

Dans cet article nous allons tour à tour aborder :

  • les avantages que peut tirer un projet ou une entreprise en adoptant SOA,
  • l'écriture de composants SOA : pour cela nous allons concevoirde façon progressive un exemple de framework SOA.

Nous allons par la suite brièvement aborder l'assemblage de composants SOA puis à la fin nous allons nous poser la question sur la pertinence du mode SOA.

II. Quels sont les avantages lorsqu'on adopte SOA ?

Les avantages SOA généralement cités peuvent être classés en 2 catégories :

  • les avantages pour le développeur,
  • les avantages pour l'entreprise.

II-1. Avantages pour le développeur

Ci-dessous quelques avantages :

  • Meilleure maintenance de l'application : en effet en cas de bug il suffit de localiser le service qui cause problème, de l'isoler, de le traiter et ensuite de le réinjecter dans l'application.
  • Meilleure évolutivité par ajout de nouveaux services ou remplacement de services existants.
  • Réutilisabilité de services : des services de base peuvent être partagés entre différentes applications. Le développement de nouvelles applications peut alors être fait par combinaison de services déjà éprouvés et du coup le risque de bug s'en trouve amoindri.
  • Environnement technique de niveau relevé: les équipes de développement qui adoptent SOA sont souvent emmenées à mettre en place un Centre d'Excellence en Architecture, ce qui élève le niveau de conception et de développement des projets. Baigner dans un tel environnement ne peut qu'être bénéfique pour un développeur.

II-2. Avantages pour l'entreprise

Ci-dessous quelques avantages :

  • Agilité et innovation permanente des offres : pour une entreprise qui doit sans cesser lancer de nouvelles offres sur le marché, le développement de ces dernières peut se faire en grande partie par utilisation de services déjà existants. Cela a pour conséquence de réduire le temps de sortie des offres car le temps des développements est réduit.
  • Possibilité d'aligner les services avec les métiers : cela a pour avantage de faire travailler de façon rapprochée les équipes métiers avec les équipes techniques.
  • Mise en place d'un tableau de bord SOA permettant de recenser les services de base communs aux métiers de l'entreprise et de réfléchir sur la façon de les organiser de sorte à les réutiliser efficacement. Il pourrait en résulter la mise en place d'une politique de prévention pour ce qui concerne la prolifération de services parfois inutiles.
  • Mise en place d'une gestion rigoureuse des modifications des services partagés : cela suppose des métriques de qualité et des tests de non régression hautement éprouvés.
  • Mise en place de métriques sur l'utilisation d'un service, ce qui peut aider à évaluer son coût.
  • Enfin la SOA prépare l'entreprise à pouvoir inter-communiquer avec ses partenaires via différents canaux : EAI, ESB, etc. Elle expose ses services à ses partenaires qui les consomment.

Au-delà des avantages immédiats ci-dessus cités, certaines entreprises peuvent encore aller plus loin en réfléchissant sur une sorte de gouvernance SOA. La gouvernance SOA peut être définie comme une réflexion que l'entreprise ayant adoptée SOA mène afin de profiter pleinement de cette nouvelle architecture pas seulement du point de vue technologique, mais aussi des points de vue humain, processus, droits décisionnels (qui est-ce qui par exemple décide de faire évoluer un service et quelles sont les précautions à prendre, qui est-ce qui décide de la publication ou du retrait d'un service, etc.), gestion des objectifs, etc.

La gouvernance SOA n'est pas dans le scope de cet article.

III. Ecriture de composants SOA

Nous allons concevoir un petit framework SOA de façon progressive.

Ensuite suivra une mise en oeuvre de ce framework au travers d'un exemple de code d'un projet SOA.

III-1. Exemple de conception d'un petit Framework SOA

Nous allons partir d'une logique selon laquelle tout composant d'une application orientée services est construit par combinaison de plusieurs couches : la couche d'accès aux données (DAO pour Data Access Object), la couche des services simples (SISV pour Simple Service) et la couche des services composés (SVCO pour Service Composé).

Figure 1 : les différentes couches d'un composant
Figure 1 : les différentes couches d'un composant

En dehors des couches ci-dessus, notre logique voudrait qu'on ait aussi :

  • les DTO (data transfert objet) pour véhiculer les données entre les composants. Les DTO interviennent aussi au sein des couches d'un composant.
  • une ossature pour la gestion des exceptions.

Nous développons les briques techniques de ce framework dans la suite.

III-1-1. Les Data Transfert Object (DTO)

III-1-1-1. Définition

Les données persistantes sont les informations contenues et gérées dans les bases de données (MySQL, Oracle, SQL Server...) ou sur tout autre support (fichier Excel, XML...). Les DTO sont utilisés pour véhiculer les données à travers les couches. Ils peuvent également intervenir lors des échanges de messages entre l'application et l'extérieur.

III-1-1-2. Diagramme de classe

Figure 2 : diagramme de classe pour les DTO
Figure 2 : diagramme de classe pour les DTO
  • ITransferableObject :
ITransferableObject
Interface de base pour tous les objets qui permettent de véhiculer les données entre les couches d'un composant ou entre l'application et l'extérieur.
  • CommonTransferableObject :
CommonTransferableObject  
Classe abstraite commune à tous les Transferable Object (DTO, criteria)  
LazyLoadArrayList Tableau contenant la liste des propriétés devant être chargées par lazy-load.
FilLazyLoadProperty() Charge en lazy-load les propriétés définies dans le tableau LazyLoadArrayList.
Credential() Retourne un objet Credential qui encapsule les données d'authentification d'un utilisateur.
  • AbstractDataTransfertObject :
AbstractDataTransfertObject  
AbstractDataTransfertObject() Constructeur permettant de créer une nouvelle instance d'un objet transférable sans état. L'objet peut avoir les états suivants :
- IsNew = true pour spécifier que l'objet devra être nouvellement créé dans le en base.
- IsModified = true pour spécifier qu'il existe déjà un tel objet en base et que le système devra juste faire un update de cet objet existant avec les nouvelles données contenues dans le DTO.
- IsDeleted = true pour spécifier qu'il existe déjà un tel objet en base et que le système devra le supprimer.
Par défaut un nouveau DTO est à l'état isNew.
PropertyHasChanged() Si isModified=true alors ajoute la propriété concernée dans la liste des PropertiesModified.

III-1-2. L'accès aux données (DAO)

III-1-2-1. Définition

La DAO (Data Access Object) est la couche où sont implémentées les opérations d'accès aux données. En général ces opérations concernent la recherche, la création, la suppression ou la modification. Le Framework met en place des classes techniques pour gérer la persistance dans les applications indépendamment du support de persistance (base de données, fichier xml, etc.). En général chaque module métier a son propre DAO (Exemple : DocumentDAO, RevenuDAO, ...). Toutefois un même DAO peut être utilisé par plusieurs modules métiers.

Pour accéder aux données, la couche DAO peut s'appuyer sur un Framework de mapping objet relationnel (ORM) tel Hibernate ou un Data Mapper tel iBatis.

III-1-2-2. Diagramme de classe

Figure 3 : Couche DAO
Figure 3 : Couche DAO
  • IDataAccessObject :
IDataAccessObject
Interface de base de toute DAO.
  • IGeneriqueDAO :
IGeneriqueDAO  
  Interface générique définissant les opérations de base (CRUD) d'accès aux données.
Insert( DTO ) Insère une DTO dans la source de données et retourne son identifiant technique.
Find( Criteria ) Recherche toutes les DTO dont les critères de recherche sont précisés dans un Criteria.
FindOne( DTO ) Charge une DTO ainsi que toutes les propriétés spécifiées dans le tableau du lazy-load
FindAll() Retourne toutes les DTO d'un type présent dans la base de données.
Update( DTO ) Met à jour une DTO dans la source de données. Pour identifier la DTO, le framework s'appuie soit sur l'identifiant technique, soit sur la BK (BK pour Business Key).
SaveOrUpdateOrDelete( DTO ) Insère, met à jour ou supprime un DTO en fonction de son état.
  • KTIbatisGenericDAOImpl :
KTIbatisGenericDAOImpl
Implémentation générique d'une DAO s'appuyant sur le data mappeur Ibatis pour l'accès aux données.
  • DAOController :
DAOController
Gère l'accès aux DAO : on y trouve la méthode get( String key ) permettant de retourner l'implémentation de la DAO dont la clé est passée en paramètre

III-1-3. Les services simples (SISV)

III-1-3-1. Définition

Les services simples sont constitués de fonctionnalités métiers basiques (atomiques). Ils forment une couche qui manipule directement la couche DAO. Cette couche est en général très stable et est aussi souvent désignée par 'couche des composants métiers'. Nous allons désigner les services simples par l'abréviation SISV.

III-1-3-2. Diagramme de classe

Figure 4 : Couche SISV
Figure 4 : Couche SISV
  • IBusinessService :
ISimpleService
Interface de base de tout service simple.
  • IGeneriqueSISV :
IGeneriqueSISV  
Créer( DTO ) crée une DTO en base et retourne son identifiant technique.
Charger( DTO ) Retourne une DTO en chargeant toutes les propriétés marquées en lazy-load.
Chercher( Criteria ) Retourne une liste de DTO selon des critères précisés dans Criteria.
Enregistrer( DTO ) Permet de faire des opérations sur des grappes d'objet. La méthode Enregistrer est invoquée à la racine de la grappe et tous les objets de la grappe sont traités en fonction de leurs états.
  • SISVFinder :
SISVFinder
Il s'agit d'un finder pour l'accès aux SISV : on y trouve la méthode get(String key) permettant de retourner l'implémentation de la SISV dont la clé est passée en paramètre.

III-1-4. Les services composées (SVCO)

III-1-4-1. Définition

C'est la couche des services de haut niveau. Ces services sont obtenus par combinaison des fonctionnalités offertes par la couche des services simples (SISV). Nous les désignons aussi par services composés, en abrégé SVCO.

Exceptionnellement pour des composants pas complexes, un SVCO peut directement manipuler des DAO.

III-1-4-2. Diagramme de classe

Figure 5 : Couche SVCO
Figure 5 : Couche SVCO
  • IServiceComposed :
IServiceComposed
Interface de base de tout service de haut niveau (SVCO).
  • IGeneriqueSVCO :
IGeneriqueSVCO  
Créer( DTO ) crée une DTO en base et retourne son identifiant technique.
Charger( DTO ) Retourne une DTO en chargeant toutes les propriétés marquées en lazy-load.
Chercher( Criteria ) Retourne une liste de DTO selon des critères précisés dans Criteria.
Enregistrer( DTO ) Permet de faire des opérations sur des grappes d'objet. la méthode Enregistrer est invoquée à la racine de la grappe et tous les objets de la grappe sont traités en fonction de leurs états.
  • SVCOFinder :
SVCOFinder
Il s'agit d'un finder pour l'accès aux SVCO

III-1-5. La gestion des exceptions

III-1-5-1. Définition

Le framework offre un socle technique permettant de gérer les exceptions. Pour lever une exception dans une méthode, il suffit de décorer ladite méthode avec l'attribut MethodAttribut. MethodAttribut prend en paramètre la clé du message d'erreur ainsi que les arguments du message d'erreur. Une methode peut lever une ou plusieurs exceptions.

 
Sélectionnez

[MethodAttribut("KTMICROFI_USERSVCOIMPL_CHARGER")]
 public UserDTO Charger(UserDTO userDTO)
 {
			userSISVImpl = ConcreteSISVImpl(userDTO);
            if (userSISVImpl.ValiderUserDTO(MODE.FINDONE.ToString(), userDTO))
           {
              return userSISVImpl.Charger(userDTO);
           }
           return null;
 }

III-1-5-2. Diagramme de classe

Figure 6 : Diagramme de classe de gestion des exceptions
Figure 6 : Diagramme de classe de gestion des exceptions
  • ExceptionAdvice :
ExceptionAdvice
Intercepteur des appels des méthodes.
  • IExceptionHandler :
IExceptionHandler  
Interface de base des classes chargées de traiter les exceptions.  
HandlerException ( System.Exception, Object, MethodeInfo , Object[]) Méthode dans laquelle le traitement de l'exception est effectué. Elle prend comme paramètres :
- System.Exception : représente l'exception levée.
- Object : Objet cible sur lequel la méthode est définie.
- MethodeInfo : contient les méta-données ( nom de la méthode, type de retour,...) de la méthode en cours d'execution.
- Object[] : tableau contenant les arguments de la méthode.
  • DefaultKTExceptionHandler :
DefaultKTExceptionHandler
Une implémentation par défaut de l'interface IExceptionHandler.
  • IMethodMatcher :
IMethodMatcher  
GetExceptionHandlerKey (MethodInfo, object[]) Retourne la clé du gestionnaire d'exception qui traitera l'exception lévée :
- MethodInfo : méta-données décrivant la méthode dans laquelle l'exception a été lévée.
- Object[] : tableau contenant les arguments de la méthode.
  • DefaultKTMethodMatcher :
DefaultKTMethodMatcher
Une implémentation par défaut de l'interface IMethodMatcher.
  • IKeyReader :
IKeyReader  
ReadKey ( MethodInfo, object[]) Retourne la clé du message d'erreur pour l'exception à traiter :
- MethodInfo : méta-données décrivant la méthode dans laquelle l'exception a été levée.
- Object[] : tableau contenant les arguments de la méthode.
  • DefaultKTReader :
DefaultKTReader
Une implémentation par défaut de l'interface IKeyReader.
  • IExternalExceptionBuilder :
IExternalExceptionBuilder
Permet de contruire une exception en fonction du mode d'exposition du service (SOAPexception pour les WebServices...)
  • DefaultKTExternalExceptionBuilder :
DefaultKTExternalExceptionBuilder
Une implementation par défaut de l'interface IExternalExceptionBuilder.

III-2. Exemple d'utilisation du Framework SOA dans un projet

III-2-1. Spécifications du projet

Nous allons mettre en oeuvre le framework en codant un composant SOA dans le cadre d'un petit projet de gestion d'une mini banque de détail : le projet MiniBank.

L'application MiniBank doit :

  • permettre à un Agent de créer ou rechercher les comptes clients.
  • permettre à un Client de visualiser les historiques de toutes les opérations effectuées sur son compte bancaire.
  • permettre à un client de payer ses achats e-commerce. La Mini-Bank exposera alors un service au site e-commerce que ce dernier pourra invoquer pour interroger le compte bancaire du client et effectuer des débits correspondants au montant des achats.

Le projet MiniBank va comporter une partie front-end et une partie serveur.

Nous allons coder le serveur en mode SOA.

A première vue on distingue le service des Comptes Bancaires : CompteBancaireService. Ce service devra fournir les fonctionnalités suivantes :

  • fonctionnalité de création d'un compte bancaire pour un client donné. On suppose qu'il n'existe qu'un seul type de compte bancaire et que la création d'un compte bancaire se fait de pair avec la création du client si ce dernier n'existe pas déjà dans la liste des clients de la banque.
  • fonctionnalité de recherches de comptes bancaires.
  • fonctionnalité de visualisation de l'historique d'un compte bancaire.
  • fonctionnalité consistant à mouvementer le compte bancaire. Mouvementer un compte bancaire consiste à le créditer ou à le débiter.

Dans la suite nous allons tour à tour aborder :

  • les DTO.
  • les services simples (SISV) et les DAO que va utiliser notre CompteBancaireService..
  • l'exposition des fonctionnalités de CompteBancaireService.

III-2-2. Les DTOs

III-2-2-1. Diagramme de classe : module User et module Compte Bancaire

Image non disponible
Figure 7 : les DTO de la MiniBank

Dans le schéma ci-dessus,

  • BK désigne la business key : cela se traduira en base par une contrainte UNIQUE NOT NULL.
  • PK désigne l'identifiant technique : cela se traduira en base par PRIMARY KEY.
  • FK désigne l'identifiant de l'entité d'une association : cela se traduira en base par FOREIGN KEY.
  • Lazy load signifie chargement à la demande. Si lazy-load=false alors le chargement a lieu automatiquement et on utilise une jointure afin d'éviter d'avoir N+1 requêtes (avoid N+1 request).

AgentDTO est le DTO pour représenter un Agent. ClientDTO représente un Client.

AgentDTO et ClientDTO étendent UserDTO : on a utilisé la stratégie JOINED-SUBCLASS.
Pour cela au niveau de UserDTO on a mis toutes les propriétés communes à ClientDTO et AgentDTO ainsi que le discriminateur (ici c'est la propriété 'rôle'). UserDTO va se traduire dans le code par une classe abstraite.

Entre ClientDTO et CompteDTO nous avons une relation bidirectionnelle de type 1<->n : 1 client a potentiellement plusieurs comptes bancaires et réciproquement un compte bancaire appartient à 1 Client.
Le côté maître de la relation bidirectionnelle ci-dessus est au niveau de CompteDTO et le côté esclave (dans certaines littératures technique on parle de côté mapped-By) est au niveau de ClientDTO.

III-2-2-2. Exemple de code d'une classe DTO

III-2-3. La couche DAO

III-2-3-1. Diagramme de classe : module comptes bancaires

Image non disponible

III-2-3-2. Exemple de code d'un Service Simple

III-2-4. La couche des Services Composés

III-2-4-1. Diagramme de classe : module Compte Bancaire

Image non disponible

III-2-4-2. Exemple de code d'un Service Composé

III-2-5. Exposition des services composés

IV. Infrastructure SCA pour l'assemblage de composants SOA

Une fois qu'on a fini de développer nos services dans une technologie donnée (Java, .NET, C++, etc.), il se pose naturellement les questions d'assemblage.

L'assemblage peut être vu comme une technique consistant à définir d'une part comment un composant expose ses services pour consommation par d'autres applications à priori développées dans des technologies hétérogènes, et d'autre part comment un ensemble de services peuvent être packagés sous formes de composites.

Le fait de mettre en place des composites permet de favoriser la réutilisation dans la mesure où on expose un groupe de composants comme un seul service.

Il existe des spécifications SCA d'assemblage qui décrivent comment lier les composants et les composites.

Avec un assemblage SCA en place :

  • Le développeur dispose d'un point d'accès (on parle aussi d'interface) unique pour les services. L'accès à un service est rendu possible par les mécanismes d'accès proposés par l'infrastructure SCA. Notons que les services peuvent être implémentés dans n'importe quel langage.
  • L'administrateur, par configuration de la plate-forme SCA, peut mettre en place une politique efficace pour sécuriser l'accès aux services ou plus globalement configurer un ensemble de politiques pour des services.

Il existe un ensemble de plate-forme SCA open source. Parmi celles-ci, on peut citer notamment les plates-formes :

Dans une publication ultérieure nous ferons un tutorial d'assemblage de composants à l'aide de Tuscany.

V. Débat : le mode SOA est-il toujours adapté et pertinent pour nos développements ?

De nos jours de nombreuses entreprises ont adopté du mode SOA pour les développements de leurs projets et plus largement pour leur SI.

Cela a entraîné parfois de gros investissements car la mise en place d'une politique SOA a nécessité de profonds chamboulements.

Une des questions qu'on peut légitimement se poser est de savoir si les entreprises ont toujours un bon retour sur investissement. De notre point de vue, les entreprises qui sont confrontées à une rude concurrence nécessitant qu'elles renouvellent sans cesse leurs offres ont certainement trouvé leur compte dans la SOA du fait par exemple de l'agilité et de la réutilisation de services lors des développements de nouvelles offres.

Pour ce qui concerne les entreprisses qui n'ont pas besoin de systématiquement remettre en cause leurs offres ou n'ont pas trop besoin d'agilité, elles ont surtout trouvé dans la SOA un moyen de rendre leur système d'information propre et lisible. En adoptant la SOA, elles se préparent à inter-communiquer via des bus EAI ou ESB avec leurs partenaires avec une relative aisance technique.

En dernier lieu les entreprises qui ne sont pas confrontés aux enjeux majeurs ci-dessus cités auront certainement plus de mal à trouver le bien-fondé d'une politique SOA...

Ces débats sur le retour sur investissement SOA ont parfois emmené certains gourous techniques à décréter la mort de SOA...

Au-delà du débat sur la mort ou pas de SOA, certaines éminences telles Rockford Lhotka (www.lhotka.net) ont émis il y'a quelques années l'idée selon laquelle la SOA n'est pas quelque chose de nouveau car en réalité c'était déjà pratiqué de tout temps ! Par ailleurs il va à l'encontre du principe de réutilisabilité chère à SOA arguant que cella baisse les performances de l'application.

VI. Les téléchargements

Le projet Maven du framework que nous avons conçu dans cet article est disponible ici en version .NET : ??
Il a été fait en s'appuyant sur une adaptation de Maven pour .NET selon l'article suivant : Maven pour les projets .NETMaven pour les projets .NET

Le projet UML du serveur de la MiniBank est disponible ici : ??.
Il a été fait en s'appuyant sur l'article UML suivant : Framework de modélisation d'un projet en langage UML

Une version .NET du projet Maven de la MiniBank (réalisée Spring.net et iBatis.net) est disponible ici : ??

Koossery Technology est une entreprise spécialisée dans les développements de projets au forfait (.NET & Java J2ee) et dispose pour cela d'une cellule Architecture Technique & Outillages ainsi que d'une Usine Logicielle.

Koossery Technology dispose aussi d'un Centre de Services pour les développements GED à base de Alfresco et workflow avancés à base de jBoss jBPM.

Tous nos remerciements au comité developpez.com