Parametri delle classi a livello di servizio: primitive o tipi specifici di dominio?

3

Abbiamo un servizio web esistente che è attualmente modellato come un singolo progetto, in cui le classi di web / service / manager / modello sono diventate un po 'confuse e mescolate. Come refactoring, stiamo separando i pezzi per la manutenzione a lungo termine.

Mentre ci prepariamo, ho assemblato un elenco di "linee guida" da applicare al design del livello di servizio, e uno di questi è:

Service-level parameters should **only** be primitives.

Significato con una classe (codice pseudo-Java):

class Account {
    public enum AccountType {
        PERSONAL, BUSINESS
    }

    public Account(String name, AccountType type) { ... }
}

Il tuo metodo di servizio potrebbe essere simile a:

class AccountService {
    public Result createAccount(String accountName, String accountType) {
        AccountType type = AccountType.valueOf(accountType);
        // TODO: Business-level validation for duplicate names, etc.
        Account newAccount = new Account(accountName, type);
        return accountManager.save(newAccount);
    }
}

La mia domanda è: quel parametro accountType a livello di servizio dovrebbe essere un primitivo, o dovrebbe essere il AccountType enum type?

Ragioni per le primitive:

  • Il livello di servizio può eseguire la convalida se gli assegni un tipo non risolvibile
  • Se migri i tipi, il livello di servizio può fornire la compatibilità all'indietro

Motivi per i tipi di modello:

  • Durante la scrittura di test a livello di servizio, un AccountType errato è un errore di compilazione
  • Le affermazioni casuali che creano tutti i tipi di account non funzionerebbero se ne manchi una (ad esempio aggiungi un nuovo AccountType e dimentica di scriverne un test)

La domanda che cerco di pormi è se entrambe le API REST e di uno strumento CLI sono posizionate sopra il livello di servizio, cosa le rende facili da scrivere?

Quindi, c'è una preferenza / principio guida qui?

    
posta Craig Otis 03.05.2018 - 15:26
fonte

2 risposte

2

Fondamentalmente, esistono tipi per fornire informazioni sui valori. Quindi, quando si progetta un'API, il principio generale è abbastanza intuitivo:

Argument and return types should not lie about the values they support.

Ad esempio, se il metodo di servizio ipotetico espone un argomento intero, dovrebbe accettare qualsiasi numero intero.

Se accetta solo 4 valori, in realtà non accetta un numero intero, accetta la codifica dei numeri interi di un altro tipo di dati, quindi dovrebbe essere il proprio tipo enumerato.

Dove diventa complicato è se i valori accettati coprono un ampio intervallo di valori. Ad esempio, se i valori validi sono compresi tra 20 e 100, varrebbe la pena di creare un tipo per esprimere tale limite?

Qui diventa un giudizio, e posso solo offrire le mie regole empiriche - hanno lavorato per me finora, ma non farò alcuna pretesa di universalità.

Principio 1: si tratta di un modello di dominio nascosto?

Questa limitazione è specifica per questo metodo o descrive un intervallo di valori che si associano a un tipo di dati significativo.

Ad esempio, se il metodo accetta valori float tra 0 e 100, questo è intrinseco a questo metodo di servizio o sta effettivamente modellando il completamento percentuale di qualche processo?

Generalmente preferisco esporre i modelli di dominio, poiché aumentano la quantità di aiuto che il compilatore può darti e diminuiscono la quantità di difficoltà che i nuovi manutentori hanno esplorato il codice.

Principio 2: quanti posti è rilevante questa restrizione?

La creazione di un tipo per esprimere l'intervallo di valori validi non è gratuita. Il costo effettivo varia in base alla lingua (es: Scala rende molto più semplice di Java), ma è diverso da zero.

Per questo motivo, dobbiamo tenere a mente i benefici che otterremo dall'aggiungere questo nuovo tipo.

Se il valore viene utilizzato sempre e solo direttamente nel metodo di servizio, probabilmente non vale la pena di inserirlo in un tipo.

Se il valore viene passato dappertutto, ha senso convalidarlo una volta e avvolgerlo in un tipo, quindi è facile da eyeball quando un metodo funziona con un valore convalidato e non è necessario ripetere la convalida.

Principio 3: quanto è complessa la firma del metodo?

Se la firma del metodo include esecuzioni di diversi parametri dello stesso tipo, spesso vale la pena di includerli nei tipi per consentire al compilatore di rilevare se l'ordine viene incasinato.

    
risposta data 06.05.2018 - 18:16
fonte
2

Bene, di che tipo di domini stiamo parlando? Se il consumatore sarà basato su java e la definizione è anch'essa basata su java, allora userei un enum, perché il tuo metodo ha un insieme di vincoli statici che sono rappresentati dai valori del tuo enum non solo un int.

Se si trattasse di un'interfaccia web, come l'API di REST, userei l'una o l'altro, ma probabilmente mi piace usare enumerazioni ancora una volta per gli stessi motivi e perché Swagger per esempio funziona molto bene con loro.

    
risposta data 03.05.2018 - 15:35
fonte