Traduction▲
Cet article est la traduction la plus fidèle possible de l'article original de Manuel Felicio, Architecting Silverlight LOB applications (Part 3) - Domain Services and Repository.
Architecturer des applications métier Silverlight - Services de domaine et repository▲
Dans le précédent article, j'ai décrit une vue d'ensemble d'une architecture qui pourrait être utilisée lors du développement d'applications modulaires Silverlight. Dans cet article je vais démarrer avec un modèle de domaine métier simple et je vais créer une couche de domaine autour de ce modèle.
Dans cet exemple notre application sera une application Web pour une société qui vend des produits et qui peut être utilisée par ses clients pour passer des commandes et par ses employés pour les traiter. Ces commandes contiennent des détails qui consistent en des produits et des quantités. Chaque produit peut avoir une catégorie de produit. Quand un client soumet une commande, un employé de la société peut traiter cette commande et préparer les articles pour l'expédition.
La société a quelques départements et chaque employé fait partie d'un département. Cela signifie que les employés du service des ventes devront traiter les commandes ; les employés du département des ressources humaines pourront recruter de nouveaux employés ; et les employés du département des finances pourront observer quelques graphiques et imprimer des rapports avec des statistiques sur comment évoluent les ventes avec le temps, des rapports de ventes, etc.
L'authentification et l'autorisation seront par la suite ajoutées.
Par souci de simplicité, j'ai créé le modèle de données suivant :
Ce modèle sera tant notre modèle de données que notre modèle de domaine. C'est très simple et comme je l'ai dit dans le précédent article, créer un modèle de domaine séparé est facultatif car vous pouvez toujours bien vous en sortir avec vos entités de données. Puisque nous utilisons Entity Framework nous allons tirer profit de LinqToEntitiesDomainService, un service de domaine spécifique qui traite des objets de l'Entity Framework.
Puisque nous commençons par notre couche de domaine, nous allons définir un service de domaine qui peut être utilisé depuis notre application Silverlight (ou d'autres clients également) et créer les opérations CRUD. Je suis sûr que vous avez remarqué combien de code (dupliqué) est généré lorsque vous utilisez le modèle de service de domaine de Visual Studio. Eh bien… plus besoin de souffrir ! Créez tout simplement une classe de base pour vos services de domaine et ajoutez les méthodes suivantes :
#region
Basic
CRUD
protected
IQueryable<
T>
GetQuery<
T>
()where T
:
class
{
return
this
.
GetRepository<
T>
().
GetQuery
();
}
protected
void
Insert<
T>
(T entity)where T
:
class
{
this
.
GetRepository<
T>
().
Add
(entity);
}
protected
void
Update<
T>
(T entity)where T
:
class
{
this
.
GetRepository<
T>
().
AttachAsModified
(entity,
this
.
ChangeSet.
GetOriginal
(entity));
}
protected
void
Delete<
T>
(T entity)where T
:
class
{
this
.
GetRepository<
T>
().
Delete
(entity);
}
#endregion
protected
IRepository<
T>
GetRepository<
T>
()where T
:
class
{
return
this
.
Container.
Resolve<
IRepository<
T>
>
();
}
La propriété Container est une instance de IUnityContainer, que je garde dans le service de domaine de base. J'ai d'habitude une classe singleton commune où le conteneur IoC est disponible mais je crée aussi un conteneur enfant pour chaque service de domaine. Cela me permet d'enregistrer des instances spécifiques dans le conteneur enfant et de l'utiliser uniquement dans la portée de mon service de domaine. C'est utile pour résoudre des repositories parce que mes repositories dépendent de l'ObjectContext qui est injecté via l'injection de dépendances. Voici comment on procède :
public
abstract
class
DomainRepositoryService<
TContext>
:
LinqToEntitiesDomainService<
TContext>
,
IDbContextManagerwhere TContext
:
ObjectContext,
IDbContext,
new
(){
protected
IUnityContainer Container{
get;
private
set;
}
///
<
summary
>
///
Create
a
child
container
to
use
THIS
as
the
IDbContextManager
implementation
when
resolving
repositories
///
Ensures
that
other
threads
/
domain
services
can
use
the
main
container
to
resolve
repositories
///
and
use
its
own
instance
as
the
IDbContextManager
for
the
repositories
they
need
///
<
/
summary
>
///
<
param
name
=
"
context
"
>
The
DomainServiceContext
instance
<
/
param
>
public
override
void
Initialize
(DomainServiceContext context){
base
.
Initialize
(context);
this
.
Container=
IoC.
Current.
Container.
CreateChildContainer
();
this
.
Container.
RegisterInstance<
IDbContextManager>
(this
);
}
#region
IDbContextManager
Members
public
IDbContextGetDbContext
(){
return
this
.
ObjectContext;
}
#endregion
//
...
}
Ceci dit, le service de domaine spécifique pour notre application Silverlight ressemblera à ceci :
[
EnableClientAccess
()]
public
class
MyAppService:
DomainRepositoryService<
MyAppEntities>
{
#region
Customer
public
IQueryable<
Customer>
GetCustomers
(){
return
base
.
GetQuery<
Customer>
();
}
public
void
InsertCustomer
(Customer entity){
base
.
Insert
(entity);
}
public
void
UpdateCustomer
(Customer entity){
base
.
Update
(entity);
}
public
void
DeleteCustomer
(Customer entity){
base
.
Delete
(entity);
}
#endregion
//
repeat
for
the
other
entities...
}
Remarquez comment ma couche de domaine ne se soucie pas vraiment de comment les entités sont stockées ou gérées avec EF (Entity Framework). Il délègue tout simplement la responsabilité à un objet qui implémente l'interface IRepository.
Cette interface peut être générique et ressemble souvent à :
public
interface
IRepository<
TEntity>
where TEntity
:
class
{
TEntity
GetEntity
(int
id);
IQueryable
<
TEntity>
GetQuery
();
void
Add
(TEntity entity);
void
Update
(TEntity entity);
void
Delete
(TEntity entity);
///
<
summary
>
///
Abstraction
on
RIA
'
s
AttachAsModified
extension
method
///
<
/
summary
>
///
<
param
name
=
"
current
"
>
<
/
param
>
///
<
param
name
=
"
original
"
>
<
/
param
>
void
AttachAsModified
(TEntity current,
TEntity original=
null
);
}
Pour la simplicité, j'ai inclus la méthode AttachAsModified dans l'IRepository. Cette méthode est importante parce que RIA exige que l'entité en cours de mise à jour soit attachée dans l'ObjectContext. Une autre option serait de créer une interface séparée qui dérive d'IRepository et d'y ajouter la méthode. C'est que l'interface IRepository est un concept générique qui peut être utilisé avec plusieurs technologies d'accès de données.
Notez que dès que vous créez votre service de domaine et exposez vos entités, tout de suite une autre personne/équipe peut commencer à construire l'application utilisant les entités exposées, tandis qu'une autre équipe continue à travailler sur la couche serveur ajoutant des métadonnées et/ou la logique de validation qui sera également disponible pour l'application cliente.
Conclusion▲
Dans le prochain article, j'aborderai la spécification des métadonnées pour nos entités et cela devrait être suffisant pour la couche côté serveur, pour l'instant.
Liens▲
Pour terminer, je tenais à dire que tout le code est disponible sur http://code.google.com/p/mfelicio-wordpress sous trunk/2010/SL-MVVM-RIA.
Remerciements▲
Je tiens ici à remercier Manuel Felicio de m'avoir autorisé à traduire son article.
Je remercie tomlev pour sa relecture technique et ses propositions.
Je remercie également ClaudeLELOUP pour sa relecture orthographique et ses propositions.