mercredi 12 septembre 2012

Unit Test et Entity Framework (ou tout autre ORM) : à oublier

Depuis quelques temps j'utilise intensément des ORM (pour Object Relationnal Mapping), des outils qui permettent de gérer facilement le mapping entre les objets métiers et la base de donnée (donc qui nous évite tout le débug des requêtes SQL et du mapping depuis leur résultats).

Lorsque l'on code avec des tests unitaires il faut "mocker" ces accès à la base : un test unitaire n'est censé ne tester qu'une "unité de dévelopement" soit un chemin d'éxecution dans une méthode. Donc si l'on veut par exemple tester sont controller il faut que toutes les classes utilisées par ce dernier soient "mockées" c'est à dire que au lieu d'envoyer l'implémentation concrète des dépendances on envoi une classe qui retourne toujours le même résultat en répondant a l'interface attendue.

Exemple :

CustomerController utilise MyDataContext (classe de Entity Framework permettant d'accéder aux données en base). Je ne peut pas le "unit tester" car MyDataContext est une implémentation concrète (= pas une interface).
Donc deux solutions s'offres à moi :
  1. Faire en sorte que MyDataContext implémente une interface et injecter la dépendance dans le constructeur (cette injction de dépendance peut être faites via Ninject)

    En "Fakant" cette interface :
    Problème : dans mon controlleur le code "_dbContext.Customers.OrderBy(c => c.Name).ToList()" est traduit en SQL par entity framework. Et parfois ces expressions linq peuvnt ne pas pouvoir être traduites (et donc une exception est levée) ce qui fait que moi test unitaire pourrait passer, mais mon code réel non (car le code de test utilise une énumération en mémoire donc tout le linq devrait marcher). Donc cette solutions est une mauvaise solution. 
  2. Enlever la dépendance entre le controlleur et le contexte en encapsulant ce dernier dans un Repository : Le controlleur ignore qu'on utilise un ORM. On appelle cette nouvelle classe un "Repository". Il est possible de trouver de nombreuses implémentations de cette classe : http://www.lmgtfy.com/?q=entity+framework+generic+repository. Mais selon moi cette approche force a créer beaucoup trop de code d'insfrastructure et n'apporte au final que de la complexité dans le dev à cause des trops nombreuse couche d'abastraction comme décrit par le célèbre bloggeur Oren Eini. Tout d'abord elle n'apporte rien vu que DbSet de EF est déjà un repository (et DbContext un UnitOfWork), et elle empèche d'utiliser pleinement toutes les fonctionnalités de EF vu que le controlleur ignore qu'il l'utilise.

Mais du coup quelle solution ?  Je dirais pour les Test Unitaire et Entity Framework : aucune. L'approche qu'il me semble la plus facile a mettre en place est certainement le test d'intégration : on ne teste plus une partie du logiciel mais un processus dans son ensemble par exemple "Je crée une commande est ce que je la vois a la fin avec les bonnes infos". Techniquement il suffit de créer une copie de la base sur un sql express en local. Ensuite de mettre en place une comparaison de schéma avec visual studio (Données -> Comparaison de schéma -> Nouvelle comparaison de schéma) pour synchronsier base de Dev et la base de Test. Puis d'encapsuler les tests dans une transaction que l'on rollback a la fin de chaques test. Le seul inconvéniant étant le temps d'éxecution des tests vu que EF initialize le modèle.



lundi 10 septembre 2012

Remi DDD Part III : Commande

En CQS une commande est un processus qui va modifier l'état global de l'application ( par example la base de donnée) sans retourner de résultats (void). Une commande va par exemple être "Vendre un produit". Vu que j'essai d'appliquer les principes du DDD, ici on aura explicitement "Vendre un produit" dans le code et non pas "Créer commande".
Les entités :
Voici la commande (qui a été crée par l'UI, web ou non) :

On peut remarquer qu'elle implémente l'interface ICommand. Cette interface ne définit aucune méthodes, elle sert juste à "flagger" une classe comme commande.
Le "command hanlder" est la classe qui va éxecuter la commande, il ne peut y en avoir qu'un et un seul par type de commande. Il va simplement insérer les données en base.
Voici l'appel :

Utilisation de Transaction avec Entity Framework 4 et Ninject pour une application Asp.Net MVC 3

Lorsque l'on a une application qui effectue de nombreux aller-retour avec la base de donnée il est important d'encapsuler les mêmes appels d'un processus dans une transaction. Ceci afin d'éviter d'avoir une applicaiton dans un état non prévu.

Exemple : vous insérer la Commande en base, et une erreur .net est levée dans le code qui insère les LigneCommande. Donc en base vous avez une commande sans lignes = état non prévu.

Dans mon projet actuel j'utilise Entity Framework 4, Asp.NetMVC 4 et Ninject pour l'injection de dépendance.

Tout se passe dans la config de ninject, je crée une transaction sql pour chaques requetes http et je rollback cette transaction si une erreur a été levée.

Donc ici on instantie le transactionscope sur OpenTransaction, en s'abonnant a l'evènement d'application Error pour faire un Rollback de la transaction en cas d'erreur.

EDIT : Attention à bien instancier le TransactionScope, il semblerai que la configuration de base lock la base (IsolationLevel.Serializable) et force un timeout de transaction faible (30 sec sachant qu'il y en a déjà un au niveau commande). Je m'en suis rendu compte en tentant de faire un select sur ma base (dans SSMS) en étant sur un point d'arret dans mon appli. Le select a fait un timeout à cause de cet isolation level.

Référence : http://blogs.msdn.com/b/dbrowne/archive/2010/06/03/using-new-transactionscope-considered-harmful.aspx

Il faut donc mieux faire

new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = IsolationLevel.ReadCommitted, Timeout = TransactionManager.MaximumTimeout })

mardi 24 juillet 2012

RemiDDD Partie II : Aggregate


Lors de la conception d'un projet en utilisant une méthode DDD, on va faire appel à la notion de Aggregate.

Un aggregate est un entité métier de base. Cette entité peut être composée d'autres entités appelées "value object" qui n'ont aucune signification sans leur entité maitre et dont la durée de vie est la même.
Exemple : Un client à une adresse, dans le cadre d'un CRM l'adresse ne veut rien dire sans le client, quand on créera/supprimera un client on créera/supprimera son adresse.

Les Aggregate/value object doivent répondre à ces exigences :
  • Seuls les Aggregates ont un Id
  • Seuls les Aggregates sont filtrables / requêtables (on ne peut obtenir l'Adresse d'un Client sans avoir son Id).
  • Les value object ne peuvent pas référencer d'autres value object, que des Aggregate (l'adresse d'un client pourra référencer un Pays).

Il faut faire attention à ne pas faire des Aggregate sur-dimensionnés embarquants la moitié de la base de donnée.
Prenons l'exemple d'une Commande, on pourrait très bien penser que c'est un value object de Client vu qu'une Commande ne veux rien dire sans le Client. Mais le fait est que dans de nombreuses interfaces on va vouloir éditer les informations de la commande, les filtrer, appliquer une offre spéciale, l'annuler ... On va donc créér un Aggregate "Commande" qui va avoir une référence vers l'Aggregate "Client", les cas d'utilisation ont forcé cette promotion.


Le design des Aggregate se base sur ces cas d'utilisations (d’où leur utilisation en DDD). Lors de leur conception il faut se demander "Est ce que l'on aura besoin de le référencer tout seul quelque part ?" , "Est ce qu'une interface permettra de le mettre à jour ?" ...

Si vous voulez en savoir plus je vous incite à lire les documents écris par Vaughn Vernon : http://dddcommunity.org/library/vernon_2011

RemiDDD, part I

Aujourd'hui je lance mon "framework" de développement qui s'appelle RemiDDD (original).

J'ai récemment lu pas mal d'articles sur le Domain Driven Design (aka DDD), le Command-Query Responsibility Segregation (aka CQRS). Et ça a bien modifié ma manière de voir le développement d'applications métier .

Je vais donc essayer de décrire ces deux notions (ou plutôt d'en donner ma description qui n'est certainement pas la meilleure).

DDD est une manière de concevoir son application guidé par le métier (Domain).

Petit exemple d'un projet dans une démarche classique :
- Le client va demander "Lors du click sur ce bouton l'internaute va passer sa commande", le technicien créer la fonctionnalité "créer une commmande".
- Le client veut recevoir un mail lorsque le client passe une commande. Le développeur va donc envoyer un mail dès qu'on crée une commande.
- Ensuite le client veut pouvoir ajouter dans la base une commande faite par téléphone. Mais le problème est ce du coup le client va recevoir le mail de commande alors qu'il vient de la rentrer lui même. Donc le développeur va mettre un "if(OrderNotFromWeb)". 2 ans plus tard e développeur referra entièrement la fonctionnalité CreateOrder car il a 200 if a gérer et il en oubliera certainement 30 en réécrivant son code.

La complexité d'un modèle (qui est le reflet du domaine dans le logiciel) est souvent du au mélange des rôles. Par exemple ici la fonction "CreateOrder" sera implicitement responsable de "PassOrder" parce que au début cela semblait évident mais lorsque l'on sépare ces deux notions, c'est la catastrophe.
Le soucis ici est que les développeur et le client ne parle pas le même langage. DDD consiste a établir un langage commun (le "ubiquitous language") sur lequel va se baser leur travail. Ce langage commun est utilisé dans tout les projets (DDD ou non ) mais dans un projet DDD il va être le centre de toutes les attentions et si c'est correctement mis en place, un client pourra dire exactement ce que fait la fonction "CreateOrder" : créer une commande, pas réduire le stock, pas mailer les commerçants, pas vérifier le stock.

Pour permettre la création d'un modèle solide , il va falloir se baser sur plusieurs notions :
  • Aggregate Roots, Value Object qui sont le cœur du modèle, ils ne vont pas être uniquement des DTO mais aussi avoir un vrai comportement (la loi de Demeter est un bon point de départ pour savoir si l'ont doit créer une méthode dans un objet ou le manipuler directement dans la méthode courante)  :
    • Aggregate : un objet identifié, c'est donc sur lui que l'on va faire des actions du type mise à jour, suppression, insertion (exemple : Order)
    • ValueObject : une valeur non identifié qui compose un aggregate, il peut référencer un autre aggregate, mais il ne peut pas référencer un autre valueobject.(exemple : OrderLine)
  • Comand - Query Separation : Deux modèles un modèle en écriture et un modèle en lecture. 
    • Command : modifie l'état du modèle, ne retourne rien (void)
    • Query : retourne une partie de l'état du modèle
  • Comande/Query Handler qui executeront les command/query. Cette séparation ne me semble pas essentielle mais permettra de regrouper dans un même handler des commandes/query proche. Les Command/Query ne servent en fait que a décrire l'action à faire ou la requête à envoyer, les handler feront le travail a proprement dit (comunnication BDD, appel méthodes aggregates ...)
  • Les évènements/observateurs : Ils permettront d'appliquer les demandes du client commençant par "quand vous faites ça faites ...", tout en n'empiétant pas sur les process déjà existant (et debuggé). Par exemple sur le cas du mail client on aura
    • PassOrderCommand qui fera le processus de passer une commande
    • OrderPassedEvent qui sera levé a la fin du process de la commande avec les informations de la commande (prix, coord client ...)
    • SendClientMail qui observera le OrderPassedEvent qui enverra le mail au client pour confirmer sa commande (et qui peut etre appelé directement pour réexpédier le mail).
    • SendSellerMail qui observera le OrderPassedEventqui enverra le mail au vendeur pour lui signifier la commande (on peut imaginer un CrmConnection qui communiquera la vente au CRM du client ...)
  • Views : qui seront l'interface entre l'utilisateur et le client. Qui déclenche des actions sur le controller.
  • Controller qui traite les demande de l'utilisateur, les valides, et crée en conséquence les commande/query nécessaires.

 Ces classes nécessitent un peu d'infrastructure (par exemple pour lever un évènement, gérer une commande ...)  :
  • Pour les Vue / Controller des framework existent et sont très bien fait (dans les exemples j'utiliserai principalement Asp.Net MVC 3)
  • Pour les évènements / observateur , command/commandhandler, query/queryhandler, je développerai moi-même l'infrastructure en me basant sur la Reflection et sur Ninject, les quelques framework que j'ai croisé me semblent trop complexes alors que le dev me semble minime (la meilleure raison pour faire quelque chose soit même)
  • Idem pour les Aggregate/ Value Object. La persistance sera géré via RavenDB Embedded car assez mur, performant, simple d'utilisation et requétable via du Linq (je déteste les ORM qui remplace l'apprentissage du SQL par l'apprentissage d'un autre language type HQL).
L'exemple sera certainement un jeux de rôles le plus simple possible au niveau des règles mais le plus large possible au niveaux fonctionnalités.

GitHub : https://github.com/RemiBou/RemiDDD

mercredi 7 mars 2012

About

Voila mon blog je suis deux point zéro. Je vais essayer de mettre ici ma contrepartie à l'aide que je trouve en ligne .

Je pense pas que j'évoquerai mes avis politiques, les nouvelles vidéo chouette de youtube ou ce que je vais manger. Ca va être dur mais j'écrirai pas comme si je parlais au monde entier en général ça sera comment résoudre des messages d'erreur ou des truc que j'ai réussi a résoudre..

mardi 26 octobre 2010

utiliser Castle Monorail dans un site web asp.net

Dans notre plateforme nous avons un gros site web Asp.Net (avec des webforms et web service) et il y a peu nous avons découvert Castle Monorail , qui est en gros un framework MVC pour Asp.Net genre Asp.Net MVC mais qui marche aussi avec le framework 2.0.

Pour pouvoir utilsier ce framework dans un site web asp.net voici les étapes :

1 - Copier les DLL de castle monorail


Castle.Components.Binder.dll

Castle.Components.Common.EmailSender.dll

Castle.Components.Common.TemplateEngine.dll

Castle.Components.Common.TemplateEngine.NVelocityTemplateEngine.dll

Castle.Components.Validator.dll

Castle.Core.dll

Castle.MonoRail.Framework.dll

Castle.MonoRail.Framework.Views.NVelocity.dll

Castle.MonoRail.ViewComponents.dll

NVelocity.dll

Certaine ne sont pas forcément tous le temps essentielle (notamment celles sur NVelocity).

2 - Configuration :


Tout se fait dans web.config

- Déclaration de la section de configuration
[sourcecode language="xml"]<section name="monorail" type="Castle.MonoRail.Framework.Configuration.MonoRailSectionHandler, Castle.MonoRail.Framework" />[/sourcecode]
- La configuration en elle-même
[sourcecode language="xml"]
<monorail>
<controllers>
<assembly>App_Code</assembly>
<assembly>Castle.Monorail.ViewComponents</assembly>

</controllers>
<viewEngines viewPathRoot="Views">
<add type="Castle.MonoRail.Framework.Views.NVelocity.NVelocityViewEngine, Castle.MonoRail.Framework.Views.NVelocity" />
</viewEngines>
</monorail>
[/sourcecode]

On remarque le
[sourcecode language="xml"]<assembly>App_Code</assembly>[/sourcecode]

Qui déclare l'assembly App_Code (celui crée par .net à partir du dossier App_Code du site web) qui contiendra nos controller (on peut ainsi importer des assembly d'autre projet en les ajoutant ici).

les HttpHandlers (pour interdire les accès direct au vm et rediriger les .rails vers Monorail)
[sourcecode language="xml"]<add verb="*" path="*.rails" type="Castle.MonoRail.Framework.MonoRailHttpHandlerFactory, Castle.MonoRail.Framework" />
<!--block direct user access to template files-->
<add verb="*" path="*.vm" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.boo" type="System.Web.HttpForbiddenHandler" />
<add verb="*" path="*.st" type="System.Web.HttpForbiddenHandler" />
[/sourcecode]

Et le http module
[sourcecode language="xml"]<add name="monorail" type="Castle.MonoRail.Framework.EngineContextModule, Castle.MonoRail.Framework" />[/sourcecode]


3 - IIS


Dans la console d'adminstration de IIS (IIS 6 pour moi)
-Il faut aller jusqu'a la racine du site web
-Click-droit sur la racine
-Properties
-Onglet Directory
-Bouton Configuration
-Double click sur la ligne de l'extension .aspx
-Copie le chemin de l'exécutable (quelque chose comme "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll")
-"Cancel"
-Bouton "Add..."
-On colle le chemin dans la première text box
-".rails" dans la deuxième
-"verify that file exists" -> Décoché
- Ok , OK , OK

4 - TEST



Fichier TestMonorailController.cs à mettre dans le dossier App_Code (dans un sous dossier si besoin)
[sourcecode language="csharp"]
using Castle.MonoRail.Framework;

public class TestMonorailController : SmartDispatcherController
{
    public TestMonorailController()
    {
    }
    public void OnePage()
    {
        PropertyBag["toto"] = "TEST";
    }
}[/sourcecode]
Fichier OnePage.vm à mettre dans /Views/TestMonorail/ du site web
[sourcecode language="html"]$toto[/sourcecode]
Dans la navigateur on accède à
"http://laracinesdemonsite.com/TestMonorail/onePage.rails".
Et la on devrait voir s'afficher "TEST" .

Et voila.