2016-07-06 2 views
2

J'essaie de trouver un type correct pour mes identifiants dans un programme Go conçu en utilisant "Clean Architecture" de Uncle Bob Martin.Type d'ID générique pour "Architecture propre" Go program

type UserID ... 

type User struct { 
    ID UserID 
    Username string 
    ... 
} 

type UserRepository interface { 
    FindByID(id UserID) (*User, error) 
    ... 
} 

Je suivais "Clean Architecture", où le code de l'oncle Bob Martin est organisé comme un ensemble de couches (de l'extérieur dans: infrastructures, interfaces, UseCases et domaine) . L'un des principes est la règle de dépendance: les dépendances de code source peuvent uniquement pointer vers l'intérieur.

Mon type User fait partie de la couche de domaine et si le type ID ne peut pas être dépendante de la base de données choisi pour le UserRepository; Si j'utilise MongoDB, l'ID peut être un ObjectId (string), alors que dans PostgreSQL, je pourrais utiliser un entier. Le type User dans la couche de domaine ne peut pas connaître le type d'implémentation.

Grâce à l'injection de dépendances, un type réel (par exemple MongoUserRepository) implémentera l'interface UserRepository et fournira la méthode FindByID. Puisque ce MongoUserRepository sera défini dans les interfaces ou la couche d'infrastructure, il peut dépendre de la définition de UserRepository dans la couche de domaine (plus interne).

Je considère en utilisant

type UserID interface{} 

mais le compilateur ne sera pas très utile si le code dans l'une de la couche extérieure tente d'attribuer dans le type de mise en œuvre incorrecte. Je souhaite que la couche d'interfaces ou la couche d'infrastructure, où la base de données est spécifiée, détermine et requière le type spécifique UserID, mais le code de couche de domaine ne peut pas importer ces informations car cela viole la règle de dépendance.

J'ai également considéré (et je utilise actuellement)

type UserID interface { 
    String() string 
} 

mais qui suppose une connaissance que la base de données utilisera les chaînes pour ses ID (j'utilise MongoDB avec son ObjectId - synonyme de type pour string).

Comment puis-je gérer ce problème de façon idiomatique tout en permettant au compilateur de fournir une sécurité de type maximale et de ne pas enfreindre la règle de dépendance?

Répondre

1

Peut-être que vous pouvez utiliser quelque chose comme ceci:

type UserID interface { 
    GetValue() string 
    SetValue(string) 
} 

Ensuite, vous assumez que vous passez toujours et obtenir la chaîne comme un ID (il peut être la version de chaîne de caractères de l'ID entier en cas de PgSQL et d'autres SGBDR), et vous mettre en œuvre UserID selon le type de base de données:

type PgUserID struct { 
    value int 
} 

func (id *PgUserID) GetValue() string { 
    return strconv.Itoa(id.value) 
} 

func (id *PgUserID) SetValue(val string){ 
    id.value = strconv.Atoi(val) 
} 

type MongoUserID struct { 
    value string 
} 

func (id *MongoUserID) GetValue() string { 
    return value 
} 

func (id *MongoUserID) SetValue(val string){ 
    id.value = val 
} 

Je me demande si ce accomplit ce que vous voulez atteindre, mais peut-être il est plus élégant pour cacher les conversions de chaînes à l'intérieur du code d'utilisateur?

+1

Cela pourrait fonctionner. Je vais y réfléchir.(C'est en quelque sorte où je me dirigeais avec mon '' 'type UserID interface { String() chaîne }' ''). – Ralph

+0

Je ne pense pas que cela fournisse la vérification du type de temps de compilation. Une méthode 'FindByID' de type' PgUserRepository' peut également accepter une chaîne 'MongoUserID'. Ou tout type qui implémente l'interface 'UesrID'. – abhink

+0

@abhink Si vous avez une méthode spécifique au Pg, pourquoi utiliser l'interface au lieu du type struct comme type d'argument dans la méthode FindByID? – Nebril