Architecturer des applications métier Silverlight


précédentsommairesuivant

Partie 3 - Services de domaine et repository

Dans la partie précédente, 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 cette partie 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 :

Image non disponible

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 la partie précédente, 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
#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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
public abstract class DomainRepositoryService<TContext> : LinqToEntitiesDomainService<TContext>, IDbContextManager
    where 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 IDbContext GetDbContext()
    {
        return this.ObjectContext;
    }
    #endregion
    //...
}

Ceci dit, le service de domaine spécifique pour notre application Silverlight ressemblera à ceci :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
[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 à :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
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 la prochaine partie, 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.


précédentsommairesuivant

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2013 Manuel Felicio. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.