dove mettere i metodi che manipolano gli oggetti

3

Ho un metodo di controllo come segue:

public class RoomsController {

@RequestMapping(method = RequestMethod.GET, path="/v1/rooms/{name}")
    public ResponseEntity<?> getRoomInformation(@PathVariable String name, @RequestHeader("Auth-Token") String token){
        try {
            Room room = roomService.findByLogin(name);
            User user = userService.findByBackendToken(token);

            if(room == null || InstantHelper.biggerThanSixHours(room.getUpdatedAt()))
                room = gitHubService.createOrUpdateRoom(name, user.getAccessToken());

            String roomJson = new RoomFormatter(room).toJson();
            joinRoom(room.getLogin(), token);

            return new ResponseEntity<String>(roomJson, HttpStatus.OK);
        } catch (ChathubBackendException e) {
            log.error(e);
            return new ResponseEntity<>("Error: "+e.getMessage(), HttpStatus.NOT_FOUND);
        }
    }

come si vede, ho questa linea, ad esempio: roomService.findByLogin(name); ma da quello che ho capito, un servizio non restituisce alcun valore, è solo eseguire qualche azione e generare un'eccezione se qualcosa va storto. Questa è la prima cosa.
Il secondo è relativo alla formattazione / join parte

String roomJson = new RoomFormatter(room).toJson();
joinRoom(room.getLogin(), token);
return new ResponseEntity<String>(roomJson, HttpStatus.OK);

Non sono sicuro che il controller debba gestire questa quantità di responsabilità, come sapere se è il momento di aggiornare le informazioni sulla sala da API, chiamare esplicitamente RoomFormatter . Sono un po 'perso qui, perché non so davvero dove metterlo. Penso che dovrei avere uno strato intermedio che non è un servizio né un repository, e questo strato dovrebbe sapere cosa fare con la stanza, formattare il JSON e così via, ma non so cosa dovrebbe essere o se c'è un modello per questo tipo di cose. Forse è ok avere queste cose nel controller ... comunque. Tutte le idee sono ben accette!

    
posta Luiz E. 11.03.2016 - 22:03
fonte

1 risposta

3

Domande di progettazione come queste spesso tendono a non avere una risposta vera o falsa. Dipende pesantemente dai componenti circostanti e dagli obiettivi più grandi. Nonostante ciò, qui alcune cose mi sembrano piuttosto chiare.

Quindi alla tua prima domanda:

Questo:

Room room = something.findByLogin(name);

è un'attività che appartiene chiaramente al livello del modello. Puoi immaginare di testare questo metodo con una suite di test unitari che tratta solo con classi di modelli.

Qui vorrei aggiungere due cose. Innanzitutto, poiché il tuo contesto è Spring, è anche una questione su come il tuo modello è reso accessibile dal contenitore dell'applicazione. Nel mondo Java, ci sono fagioli . Potrebbe essere che scrivendo "Servizio" qualcuno (o tu) stia confondendo il "servizio del ciclo di vita dell'oggetto del contenitore di applicazioni" e il "livello di servizio dell'applicazione stessa"?

In secondo luogo, si ha un servizio per i seguenti vantaggi: 1) Distinguere l'attività di tipo web necessaria, 2) gestire le transazioni (anche per servizi nidificati o modelli multipli), 3) aggiungere alcuni post automatici o azioni pre-azione. Supponendo che, un servizio è piuttosto qualcosa di più di un controller, quindi hai ragione sui valori di ritorno. È spesso una buona idea inserire un controller in un servizio.

Secondo la mia opinione personale, mi piacerebbe vedere questo:

Room room = rooms.findByLogin(name);
User user = users.findByBackendToken(token);

Inoltre, mi aspetto di avere rooms e users consegnati come bean dalle etichette AOP o dalla configurazione XML del controller dell'applicazione. Con users , sono stato particolarmente attento poiché riguarda sicurezza, autenticazione e autorizzazione, quindi mi aspetto di cercare un framework di sicurezza che mi permetta di accedere agli utenti.

Quindi alla tua seconda domanda:

Hai ragione con la sensazione fastidiosa della quantità di lavoro che un controllore deve fare. Ricorda alcune regole di base su come scrivere buone funzioni: dovrebbero fare solo una cosa chiaramente chiamata. Dovrebbero dare o un certo valore in cambio, oppure dovrebbero avere un effetto collaterale, ma non entrambi. Ovviamente quelle regole sono contestate, ma sono spesso una buona guida. Stanno contribuendo a rispettare il principio di minimo stupore (o, quantità minima di WTF al minuto ).

Quindi, se fossi un nuovo programmatore nella tua squadra e se avessi letto:

getRoomInformation(); 

allora sì, potrei essere sorpreso se la camera (o l'elenco delle camere?) fosse occasionalmente riformattata. Ma questo non significa assolutamente che devi ridisegnare il tutto. Se questo è un caso raro, e l'aggiunta di altre strutture generali non è giustificata, allora si potrebbe fare maggiore chiarezza solo con una denominazione corretta e parlante. Ad esempio, immagina questo:

@RequestMapping(method = RequestMethod.GET, path="/v1/rooms/{name}")
    public ResponseEntity<?> getRoomInformationAndUpdateXY(@PathVariable String name, @RequestHeader("Auth-Token") String token){
        try {
         ...

Bene, chiudiamo il cieco. Quindi va bene per una volta. Va bene perché il lettore non inciampa, ma lei / lui è allertato. Ancora meglio è esternalizzare la parte di riformattazione come metodo personale, mantenendo così il controller pulito.

Potrebbero esserci concetti più potenti per le azioni pre e post del controller, se ne hai bisogno. Forse uno strato di servizio wrapping, come menzionato nella prima parte, è la soluzione migliore. O con Spring, puoi usare gli intercettori: link Forse questa è la soluzione migliore. O è un sovraccarico. O non sta funzionando nella tua situazione. Non posso saperlo, ma la tua decisione inizia con considerazioni come questa.

    
risposta data 12.03.2016 - 09:19
fonte

Leggi altre domande sui tag