Principes modernes de développement: Revoir le DRY
Qui parmi nous lors de revue du code de l’un de ses collègues ou même au moment du développement d’une nouvelle fonctionnalité ou la résolution d’un bug n’a pas constaté qu’une ou une petite ou grande partie du code se répète et quelque part il faut supprimer la duplication (question du bon sens humain 😀). Le principe DRY ou ‘dont repeat yourself‘ entre dans ce cadre de suppression de duplication. Mais est ce qu’on peut affirmer que cette définition est correcte?
On n’est pas obligé parfois de dupliquer notre code pour des raisons de bonnes pratiques d’architectures, maintenance, une évolution imprévisible de la demande métier?… Dans cet article, j’essayerai à travers des exemples de vous montrer comment le DRY peut être mal compris et que la duplication n’est pas toujours mauvaise.
Un peu d’histoire
Dans leur livre the pragmatic programmer, Andy Hunt et Dave Thomas, définissent le DRY de cette manière:
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
The pragmatic programmer
Je pense que l’ambiguïté provient de la compréhension de la première partie de la définition à savoir (piece of knowledge) ou au sens DDD le domaine de connaissance (domain of knowledge).
Prenons un exemple d’une structure Address qui constitue l’adresse d’un utilisateur. On peut dire que Address constitue un objet DAO.
// MARK: - Address
public struct Address: Codable {
// MARK: - Properties
public let id: String?
public let address: String?
public let city: String?
public let firstName: String?
public let lastName: String?
}
Imaginons que nous sommes dans un design pattern MVVM, MVP, MVC… Est ce qu’on va utiliser le modèle Address pour afficher la liste d’address d’un utilisateur? Il y a des développeurs qui vont dire mais oui on veut pas répéter le modèle etc et on veut respecter le DRY!. Oui, c’est cohérent si on comprend la définition de DRY comme étant la non duplication du code. Mais on n’a oublié la notion du ‘piece of knowledge‘ ou ce que Martin Fowler appelle BoundedContext. Je cite ici un passage important dans l’article de Martin Fowler:
As you try to model a larger domain, it gets progressively harder to build a single unified model. Different groups of people will use subtly different vocabularies in different parts of a large organization. The precision of modeling rapidly runs into this, often leading to a lot of confusion
Martin Fowler Bounded Context
En effet, dans une application complexe non monolothique , utiliser un modele DAO au niveau de l’affichage de données est signe d’un couplage fort et d’une vision qui rend l’affichage de données reliés à l’infrastructure. Imaginons qu’un jour, on veut consommer nos données d’un autre modèle DAO. A ce moment je dois faire le tour de l’application pour remplacer le mode Address par un nouveau modele Address2.
La duplication n’est pas mauvaise!
Alors, l’idéal c’est d’avoir un modèle DTO (Data Transfert Object) et on effectue le mappage depuis notre codable vers ce DTO.
public protocol CustomerAddressDTOProtocol {
// MARK: - Properties
var id: String { get }
var address: String { get }
var city: String { get }
var countryCode: String { get }
var firstName: String { get }
var lastName: String { get }
}
L’idée est d’implémenter le DTO
public struct CustomerAddressDTO: CustomerAddressDTOProtocol {
public var id: String
public var address: String
public var city: String
public var countryCode: String
public var firstName: String
public var lastName: String
}
Ensuite de faire le mapping.
extension Address {
var toCustomerAddressDTO: CustomerAddressDTO {
return .init(
id: id.orEmpty,
address: address1.orEmpty,
city: city.orEmpty,
firstName: firstName.orEmpty,
lastName: lastName.orEmpty
)
}
D’un point de vue Domain, la structure Address et CustomerAddressDTO n’ont pas le même rôle et objectif même si elles partagent les mêmes attributs. Un changement dans la structure Address n’impactera pas la couche présentation de données vu celle ci utilisent seulement les données qui proviennent du DTO qui est agnostic des détails Codable, base de données Core Data, Swift Data, realm…
Conclusion
- Le principe DRY concerne la connaissance du domaine.
- Il y a des cas où la duplication de code est parfaitement correcte.