Quand je vois ces exemples de DCI, je ne vois qu'une couche d'abstraction
rajouté à un code parfaitement clair auparavant.

Il faut se méfier de notre tendance en tant que développeur à
"systèmatiser" les solutions, à "overenginerer" les solutions au nom de
l'élégance et des principes.

Je crois plus au principe du "KISS" et du "YAGNI" : ne pas écrire du code
qui n'apporte rien pour plus tard, ou qui complique.
Ecrire du code DCI, pourquoi pas, si on a une raison bien argumentée, pas
juste au nom d'un principe.

Extraire une méthode d'une ligne dans une classe, qui ne sera implémenté
qu'à un endroit … je ne vois pas l'intérêt.




Le 4 juin 2013 13:48, Guirec Corbel <[email protected]> a écrit :

> Dans le fond, si je résume ce que tu dis, tu penses que dans un projet
> c'est mieux de ne pas trop perdre de temps à se prendre la tête sur des
> problèmes de conception alors que le client, quand il verra le projet, il
> verra les détails comme la couleur du titre.
>
> Je suis d'accord avec toi. Il ne faut quand même pas oublié que
> l'architecture est importante. Si tu néglige trop ce point tu vas le payer.
> Je penses que, pour le moment, je vais laissé du code un peu moins beau et,
> lors de ma prochaine release, je ferais un peu de ménage. Ceci dit, je
> penses avoir trouvé une bonne manière de faire. J'ai une recette qui marche
> bien.
>
>
> Le 3 juin 2013 17:38, lucas di cioccio <[email protected]> a
> écrit :
>
> Hello,
>>
>> Le truc que je n'aime pas avec les présentations que j'ai vu sur DCI
>> c'est pas tant l'"extend" par objet, c'est plutôt d'avoir une classe par
>> "méthode". En fait, pour être précis, ce sont surtout les *présentations*
>> qui ne m'ont pas plues car un peu trop dogmatiques. Les "flavors" de DCI
>> sont peut-être une solution propre à pas mal de problèmes, mais l'important
>> c'est d'identifier les cas où cette approche est pertinente et ce qu'elle
>> apporte. Pour moi l'avantage de cette approche c'est de "router" vers les
>> bonnes méthodes en fonction du contenu des données. J'estime que les
>> modules apportent déjà l'encapsulation et donc DCI est redondant si le but
>> n'est que l'encapsulation.
>> Ensuite, pour "l'extend", je ne suis pas sûr que ce soit une raison
>> suffisante pour rejeter DCI. Après tout, les perfs pourries de extend sont
>> surtout un problème d'implémentation dans MRI, ce genre de truc peut varier
>> d'une implémentation à l'autre. Du reste, si tes modules sont bien isolés,
>> c'est trivial de changer du code qui fait un "extend" en une classe qui
>> prend un objet et là où tu avais un "self" tu as l'objet en question: au
>> final, foo.bar(machin) (DCI) ou Bar.new(foo).machin (contextes, cf. les
>> liens d'Olivier, j'entrevoyais un problème proche ici:
>> http://unchoke.me/blog/2012/08/15/on-states-and-behaviours/ ) c'est du
>> kiffkiff niveau lisibilité.
>>
>> A mon avis, les contextes et DCI marchent bien quand il y a plusieurs
>> prismes par lesquels regarder un objet, et que le prisme dépend d'un état
>> contenu dans l'objet, et, qui n'est pas déterminé au "compile time" (i.e.,
>> quand tu édites ton code pour du Ruby). En gros si t'as un stream
>> d'évènements JSON ou XML qui t'arrive dans une socket, et que tu dois
>> tantôt les convertir des fois en pomme, parfois en prunes, et tantôt en
>> poire ; si en plus tu as des fruits rouges et des fruits verts, ton
>> problème se résume à trouver les bonnes méthodes dans une matrice 3x2. Si
>> tu fais du code un peu soigné, tu ne vas pas faire une méthode spécifique à
>> "pomme verte" (même si c'est valable dans une première itération) pour
>> éviter que ce code path existe quand tu as une "poire". Tu ne vas pas non
>> plus écrire une sous classe pour chaque des six combinaisons fruit/couleur
>> (surtout que, dans cette analogie, tu pourrais avoir des éléments qui ont
>> plusieurs couleurs). En revanche, ce serait raisonnable d'avoir 5 modules
>> (3 qui répondent plus ou moins à une interface Fruit et 2 à Couleur) et tu
>> vas "étendre" (ou encapsuler) à la volée chaque JSON/XML que tu reçois.
>> Enfin, si tu as une méthode qui a besoin d'une couleur+d'un fruit, alors il
>> te faudra peut-être une classe/méthode/fonction qui se base sur ces deux
>> valeurs. En clair, DCI/Contextes permet de sélectionner quelles méthodes
>> sont applicables sur quelle état des données d'entrées.
>>
>> Dans le cas où tu as un framework web tel que Rails qui, pour ton bien,
>> fait le job d'encapsuler les requêtes dans le modèle correspondant au
>> contenu des données (l'URL), ça ne semble pas universellement judicieux de
>> refaire cette partie du travail. Je te conseille de démarrer "fat model"
>> puis de modulariser progressivement en faisant juste des module MyModule
>> ... end autour des méthodes qui sont liées (souvent une grosse méthode
>> plein de if/then et une panoplie de "one-line-helpers" genre
>> is_machin_chose?(bidule)).
>>
>> In fine, le but est de router les bonnes data aux bonnes méthodes et DCI
>> peut servir, fais confiance à ton XP/flair, des fois le truc genre "gros
>> sale avec des if et des else et des variables locales comparées à des
>> variables d'instances" est pertinent si tu ne trouves pas de moyen immédiat
>> de rendre ton code plus propre. Faire du Ruby ça demande de savoir fuir les
>> problèmes par la première porte de sortie, ce qui est très difficile quand
>> on est curieux (c'est du vécu). L'important c'est de se mettre un TODO:
>> refactor XYZ et d'avoir la discipline pour revenir dessus. D'expérience,
>> quand on a ce genre de questionnements quand on code c'est que soit le
>> cerveau n'est plus très frais (i.e., oui, on devient vraiment
>> temporairement plus con) soit on a l'impression d'avoir beaucoup de temps
>> devant soi et qu'on peut se permettre de procrastiner-positivement. Dans le
>> second cas, mets toi une deadline (en t'autorisant un peu de recherche
>> perso pour gagner un peu d'XP). Dans le premier cas, autant passer son
>> temps sur des problèmes moins intellectuellement difficiles mais tout aussi
>> important/utiles pour le projet, genre demander à un tiers si la couleur du
>> titre lui semble laide (ce qui risque d'avoir un impact plus important sur
>> ton projet que si tu utilises extend ou machin.new(chose)).
>>
>> Let me know si ça résonne un peu en toi :).
>>
>> --Lucas, donneur de coup de pieds au cul à toute heure :P
>>
>>
>> Le 3 juin 2013 21:35, Simon Courtois <[email protected]> a écrit :
>>
>> Concernant ton gist une recommandation, évite de faire des classes qui
>>> héritent de Struct.new.
>>> Ça crée déjà une classe donc tu ajoutes une niveau inutile.
>>>
>>> Tu peux faire ceci à la place:
>>>
>>> MaClasse = Struct.new(:a, :b) do
>>>   def plop
>>>     "toto"
>>>   end
>>> end
>>>
>>> Pour le reste question de point de vue, je trouve qu'on a plus de fierté
>>> à faire notre travail du mieux qu'on peut ;)
>>>
>>> Simon Courtois
>>>
>>> On Monday 3 June 2013 at 21:29, Guirec Corbel wrote:
>>>
>>> J'ai fait un gist avec ma solution :
>>> https://gist.github.com/GCorbel/5699834. Je penses que je suis content
>>> avec ça.
>>>
>>> Tout de même, c'est pas facile de vouloir faire son métier comme il
>>> faut. Ha que j'étais bien quand je faisais du PHP pourri qui bugait tout le
>>> temps et qui n'avait aucune architecture mais que je m'en foutais. C'est
>>> une grosse prise de tête de vouloir toujours faire au mieux. Je ne suis
>>> jamais sûre d'avoir fait du bon code...
>>>
>>>
>>> Le 3 juin 2013 13:07, Simon Courtois <[email protected]> a écrit :
>>>
>>> C'est un edge case et selon moi dans le cas du remplacement de DCI par
>>> SimpleDelegator il ne s'applique pas plus que ça.
>>> Cela dit, si on est est dans une situation de "contexte" ça me
>>> choquerait pas que InfravisionPotionModule implémente un "observe" décoré.
>>> Au final c'est ce que je fais dans Draper quand j'en ai besoin.
>>>
>>> Pour le reste, je pense que tu as raison de rester sur des Service
>>> objects, c'est une bonne façon de fonctionner.
>>>
>>> Bonne journée,
>>>
>>>
>>>
>>> Simon Courtois
>>>
>>> On Monday 3 June 2013 at 18:59, Guirec Corbel wrote:
>>>
>>> Simon, As-tu vu ça :
>>> http://devblog.avdi.org/2012/01/31/decoration-is-best-except-when-it-isnt/?
>>> Il peut y avoir quelques soucis à utiliser SimpleDelegator.
>>>
>>> Je pense que je vais continuer à faire comme je fais. Mettre de la
>>> logique dans le contrôleur et utiliser des services objects quand il y a un
>>> logique un peu plus complexe. À chaque fois qu'il y a besoin de callbacks
>>> il est mieux de le remplacer par un service object. Si jamais j'ai besoin
>>> d'utiliser un héritage je ferais un rôle à la place.
>>>
>>> Un exemple de code que j'aime bien :
>>>
>>> class MessagesController < InheritedResource::Base
>>>   def create
>>>     sender = User.find(params[:send_id])
>>>     receiver = User.find(params[:receiver_id])
>>>
>>>     notifier = MessageNotifier.new(sender, receiver)
>>>     notifier.call
>>>
>>>     MessageGeocoder.new(notifier.message).call
>>>
>>>     create!
>>>   end
>>> end
>>>
>>> class MessageNotifier < Struct.new(:sender, :receiver, :message)
>>>   def self.call(sender, receiver)
>>>     MessageNotifier.new(sender, receiver).call
>>>   end
>>>
>>>   def call
>>>     receiver.extend(CollectorRole)
>>>     #traitement
>>>   end
>>> end
>>>
>>> class MessageGeocoder < Struct.new(:message)
>>>   def self.call(message)
>>>     MessageGeocoder.new(message).call
>>>   end
>>>
>>>    def call
>>>     #traitement
>>>   end
>>> end
>>>
>>> module CollectorRole
>>>   def paintings
>>>     #find paintings
>>>   end
>>> end
>>>
>>> Je crois que c'est propre. Ça serait facile de factoriser la méthode
>>> "self.call". La différence entre un service object et un context c'est que
>>> j'ai l'impression que le context doit prendre tout le traitement faire dans
>>> l'action d'un contrôleur. Un service object fait quelque chose de plus
>>> petit et on peut en utiliser plusieurs par action.
>>>
>>> Ça reste facile à comprendre et a tester. Je ne me sens pas tyranniser
>>> par le DCI. Avec le DCI ça m'aurait gêner d' utiliser "@user = User.all"
>>> dans un contrôleur. Avec un service object je fais ce que je veux.
>>>
>>> Vous en pensez quoi?
>>>
>>>
>>> Le 3 juin 2013 12:00, Olivier El Mekki <[email protected]> a
>>> écrit :
>>>
>>> Hello,
>>>
>>> Personnellement, dans le DCI, l'idée d'avoir des méthodes disponibles
>>> sur le model uniquement lorsqu'on en a besoin m'intéresse peu : ça
>>> m'intéresse que mon code soit lisible, donc je vais utiliser des modules
>>> ou des concerns, mais peu m'importe que des méthodes soient disponibles
>>> sur mon instance alors qu'elle n'en a pas besoin.
>>>
>>> Le concept de context m'a en revanche beaucoup plus séduit, notamment
>>> parce qu'il me permet de sortir de l'enfer du STI et permet de résoudre
>>> des problèmes que le STI lui-même ne permet pas de résoudre. J'utilise
>>> aujourd'hui quotidiennement des contextes via cette implémentation :
>>> https://gist.github.com/oelmekki/474dcc99649a82986dc3
>>>
>>> L'idée est de faire gérer les validations, filtrages de paramètres et
>>> before / after save (qui seraient conditionnels) par une classe dédiée.
>>> Ça permet d'éviter d'avoir deux models (ou plus) pour gérer des
>>> validations spécifiques de ce qui est finalement une seule resource.
>>> Ça permet également de prendre en compte ce principe de rôle (sans
>>> toutefois nécessiter que le rôle soit définit dans une classe, dans
>>> mon implémentation). Parce qu'après tout, lorsqu'un admin edit un
>>> utilisateur, ce n'est pas la même chose que lorsqu'un utilisateur
>>> s'edite lui même (pas les mêmes validations, pas les mêmes callbacks,
>>> pas les mêmes attributs autorisés, etc).
>>>
>>> La documentation inclue dans ce fichier en dit plus long.
>>>
>>> Et effectivement, pas question de forcer son usage dans toutes les
>>> actions : on doit pouvoir faire un `Foo.all` si c'est tout ce qui
>>> est nécessaire :)
>>>
>>> Note : pour exécuter ce code, il faut utiliser active_record,
>>> strong_parameters et cette lib :
>>> https://gist.github.com/oelmekki/4a82dce1d9c2a5d66936
>>>
>>> On 10:40 Mon 03 Jun     , Guirec Corbel wrote:
>>> > Bonjour à tous,
>>> >
>>> > J'ai lu quelques trucs à propos du DCI (Data Context Integration) et
>>> je ne
>>> > sais pas quoi en penser. J'aimerais avoir votre avis sur la question.
>>> >
>>> > Pour ceux qui ne connaissent pas, je vous conseil de lire ceci :
>>> > http://mikepackdev.com/blog_posts/24-the-right-way-to-code-dci-in-ruby
>>> .
>>> >
>>> > J'aime bien le concept de rôle. Dans l'application que je fais j'ai des
>>> > utilisateurs et un type d’utilisateur spécial, le collectionneur. Si
>>> j'ai
>>> > bien compris le concept il faut écrire un role comme ceci :
>>> >
>>> > module CollectorRole
>>> >   def paintings
>>> >     Painting.where(user_id: self.id)
>>> >   end
>>> > end
>>> >
>>> > Et pour avoir toutes les peintures d'un collectionneur je pourrais
>>> faire
>>> > ceci :
>>> >
>>> > user = User.new
>>> > user.extend CollectorRole
>>> > user.paintings
>>> >
>>> > Je n'aurais donc que deux modèles minimaux, User et Painting,
>>> représentant
>>> > uniquement les données (présente dans la base donnée). Si je veux
>>> ajouter
>>> > un comportement à mon utilisateur je l'ajouterai dans un rôle. Je
>>> respect
>>> > donc plus facilement le SRP (Single Responsability Principle). Ça
>>> remplace
>>> > un héritage.
>>> >
>>> > Là ou j'ai du mal c'est au niveau du context et du contrôleur. Un
>>> exemple
>>> > qui fonctionne pas trop mal serait dans le cas ou je voudrais faire une
>>> > fonctionnalité qui permet aux utilisateurs de contacter les
>>> > collectionneurs. Je ferais un truc du genre :
>>> >
>>> > class UserContactCollectorContext
>>> >   attr_reader :user_from, user_to
>>> >
>>> >   def self.call(user_from, user_to)
>>> >     UserContactCollectorContext.new(user_from, user_to).call
>>> >   end
>>> >
>>> >   def initialize(user_from, user_to)
>>> >     @user_from, @user_to = user_from, user_to
>>> >     @user_to.extend CollectorRole
>>> >   end
>>> >
>>> >   def call
>>> >     @paintings = @user_to.paintings
>>> >     # traitement et envoi du mail
>>> >   end
>>> > end
>>> >
>>> > Et dans mon contrôleur j'aurais ceci :
>>> >
>>> > class MessagesController < ApplicationController
>>> >   def send_message_to_collector
>>> >     UserContactCollectorContext.call(User.find(params[:user_from]),
>>> > User.find(params[:user_to]))
>>> >   end
>>> > end
>>> >
>>> > Ce code me convient. Là ou ça ne me convient pas c'est quand je veux
>>> tout
>>> > simplement la liste des utilisateurs. Il faudrait faire un contrôleur
>>> du
>>> > genre :
>>> >
>>> > class UsersController < ApplicationController
>>> >   def index
>>> >     @users = UserAllContext.call
>>> >   end
>>> > end
>>> >
>>> > Il y a un exemple similaire ici :
>>> >
>>> https://github.com/randx/rails-dci-example/blob/master/app/controllers/documents_controller.rb
>>> >
>>> > Franchement, créé un context pour chaque action de ce type je trouve
>>> que
>>> > c'est trop. C'est plus simple de faire "@users = User.all". L'avantage
>>> > c'est que ça sépare vraiment la logique. Le contrôleur n'a pas à
>>> savoir ce
>>> > que fait le context. S'il y a un mail envoyé, un géocodage ou tout le
>>> > reste, le contrôleur ne le sait pas. C'est vraie pour l'inverse. Le
>>> context
>>> > ne sait pas ce que fait le contrôleur. On a donc un séparation franche
>>> > entre la logique métier et la partie système.
>>> >
>>> > J'ai lu le livre CleanRuby. Je penses que Jim Gay va trop loin. Il
>>> créé un
>>> > context "framework agnostic" qui appel des fonctions du contrôleur.
>>> J'ai du
>>> > mal à comprendre pourquoi il écrit un livre prônant la valeur du
>>> "Skinny
>>> > Controller" alors qu'il a du code comme ceci :
>>> > https://github.com/radiant/radiant/blob/master/app/models/page.rb.
>>> >
>>> > Qu'en pensez-vous? Est-ce que quelqu'un d'entre vous à mis en pratique
>>> ce
>>> > concept? Quels sont vos impressions?
>>> >
>>> > Merci à tous!
>>> >
>>> > --
>>> > --
>>> > Vous avez reçu ce message, car vous êtes abonné au groupe
>>> "Railsfrance" de Google Groups.
>>> > Pour transmettre des messages à ce groupe, envoyez un e-mail à
>>> l'adresse [email protected]
>>> > Pour résilier votre abonnement envoyez un e-mail à l'adresse
>>> [email protected]
>>> > ---
>>> > Vous recevez ce message, car vous êtes abonné au groupe Google
>>> Groupes Railsfrance.
>>> > Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le
>>> concernant, envoyez un e-mail à l'adresse
>>> [email protected].
>>> > Pour plus d'options, visitez le site
>>> https://groups.google.com/groups/opt_out .
>>> >
>>> >
>>>
>>>
>>> --
>>> Olivier El Mekki.
>>>
>>> --
>>> --
>>> Vous avez reçu ce message, car vous êtes abonné au groupe "Railsfrance"
>>> de Google Groups.
>>> Pour transmettre des messages à ce groupe, envoyez un e-mail à l'adresse
>>> [email protected]
>>> Pour résilier votre abonnement envoyez un e-mail à l'adresse
>>> [email protected]
>>> ---
>>> Vous recevez ce message, car vous êtes abonné au groupe Google
>>> Groupes Railsfrance.
>>> Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le
>>> concernant, envoyez un e-mail à l'adresse
>>> [email protected].
>>> Pour plus d'options, visitez le site
>>> https://groups.google.com/groups/opt_out .
>>>
>>>
>>>
>>>  --
>>> --
>>> Vous avez reçu ce message, car vous êtes abonné au groupe "Railsfrance"
>>> de Google Groups.
>>> Pour transmettre des messages à ce groupe, envoyez un e-mail à l'adresse
>>> [email protected]
>>> Pour résilier votre abonnement envoyez un e-mail à l'adresse
>>> [email protected]
>>> ---
>>> Vous recevez ce message, car vous êtes abonné au groupe Google
>>> Groupes Railsfrance.
>>> Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le
>>> concernant, envoyez un e-mail à l'adresse
>>> [email protected].
>>> Pour plus d'options, visitez le site
>>> https://groups.google.com/groups/opt_out .
>>>
>>>
>>>
>>>
>>>  --
>>> --
>>> Vous avez reçu ce message, car vous êtes abonné au groupe "Railsfrance"
>>> de Google Groups.
>>> Pour transmettre des messages à ce groupe, envoyez un e-mail à l'adresse
>>> [email protected]
>>> Pour résilier votre abonnement envoyez un e-mail à l'adresse
>>> [email protected]
>>> ---
>>> Vous recevez ce message, car vous êtes abonné au groupe Google
>>> Groupes Railsfrance.
>>> Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le
>>> concernant, envoyez un e-mail à l'adresse
>>> [email protected].
>>> Pour plus d'options, visitez le site
>>> https://groups.google.com/groups/opt_out .
>>>
>>>
>>>
>>>
>>>  --
>>> --
>>> Vous avez reçu ce message, car vous êtes abonné au groupe "Railsfrance"
>>> de Google Groups.
>>> Pour transmettre des messages à ce groupe, envoyez un e-mail à l'adresse
>>> [email protected]
>>> Pour résilier votre abonnement envoyez un e-mail à l'adresse
>>> [email protected]
>>> ---
>>> Vous recevez ce message, car vous êtes abonné au groupe Google
>>> Groupes Railsfrance.
>>> Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le
>>> concernant, envoyez un e-mail à l'adresse
>>> [email protected].
>>> Pour plus d'options, visitez le site
>>> https://groups.google.com/groups/opt_out .
>>>
>>>
>>>
>>>
>>>  --
>>> --
>>> Vous avez reçu ce message, car vous êtes abonné au groupe "Railsfrance"
>>> de Google Groups.
>>> Pour transmettre des messages à ce groupe, envoyez un e-mail à l'adresse
>>> [email protected]
>>> Pour résilier votre abonnement envoyez un e-mail à l'adresse
>>> [email protected]
>>> ---
>>> Vous recevez ce message, car vous êtes abonné au groupe Google
>>> Groupes Railsfrance.
>>> Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le
>>> concernant, envoyez un e-mail à l'adresse
>>> [email protected].
>>> Pour plus d'options, visitez le site
>>> https://groups.google.com/groups/opt_out .
>>>
>>>
>>>
>>
>>  --
>> --
>> Vous avez reçu ce message, car vous êtes abonné au groupe "Railsfrance"
>> de Google Groups.
>> Pour transmettre des messages à ce groupe, envoyez un e-mail à l'adresse
>> [email protected]
>> Pour résilier votre abonnement envoyez un e-mail à l'adresse
>> [email protected]
>> ---
>> Vous recevez ce message, car vous êtes abonné au groupe Google
>> Groupes Railsfrance.
>> Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le
>> concernant, envoyez un e-mail à l'adresse
>> [email protected].
>> Pour plus d'options, visitez le site
>> https://groups.google.com/groups/opt_out .
>>
>>
>>
>
>  --
> --
> Vous avez reçu ce message, car vous êtes abonné au groupe "Railsfrance" de
> Google Groups.
> Pour transmettre des messages à ce groupe, envoyez un e-mail à l'adresse
> [email protected]
> Pour résilier votre abonnement envoyez un e-mail à l'adresse
> [email protected]
> ---
> Vous recevez ce message, car vous êtes abonné au groupe Google
> Groupes Railsfrance.
> Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le
> concernant, envoyez un e-mail à l'adresse
> [email protected].
> Pour plus d'options, visitez le site
> https://groups.google.com/groups/opt_out .
>
>
>

-- 
-- 
Vous avez reçu ce message, car vous êtes abonné au groupe "Railsfrance" de 
Google Groups.
Pour transmettre des messages à ce groupe, envoyez un e-mail à l'adresse 
[email protected]
Pour résilier votre abonnement envoyez un e-mail à l'adresse 
[email protected]
--- 
Vous recevez ce message, car vous êtes abonné au groupe Google Groupes 
Railsfrance.
Pour vous désabonner de ce groupe et ne plus recevoir d'e-mails le concernant, 
envoyez un e-mail à l'adresse [email protected].
Pour plus d'options, visitez le site https://groups.google.com/groups/opt_out .


Répondre à