Qui la parte precedente dell’articolo: Creare una web application con Spring Boot, MongoDB, Angular 4 e TypeScript e deployarla in cloud come Microsoft Azure Webapp – Parte 8

Ok, ora abbiamo un database MongoDB per lo sviluppo installato in locale e abbiamo configurato opportunamente la nostra applicazione Spring Boot di back-end per connettersi ad esso. Iniziamo quindi ad utilizzarlo.

CREARE UN MODEL PER I NOSTRI DOCUMENTI MONGODB

Per prima cosa creiamo nel nostro progetto un nuovo package che chiamiamo “models” e al suo interno creiamo una classe astratta che chiamiamo ABaseModel, utilizzando il prefisso A per fornire evidenza immediata che si tratta di una classe astratta. Da questa classe, deriveranno tutte le entità che vogliamo storicizzare come documenti nel nostro database MongoDB.

In questa classe, per ora, definiamo soltanto un campo Id di tipo stringa che rappresenterà l’identificativo dei nostri documenti. Per questo scopo, annotiamo il campo id con @Id, un’annotazione del package “org.springframework.data.annotation.Id” di Spring-Data.

Al fine di fornire il getter e il setter per la proprietà id, oltre ad una implementazione di default dei principali metodi ereditati da Object, annotiamo nuovamente la classe con l’annotazione @Data di Lombok, come avevamo fatto per il ViewModel.

Un’ultima considerazione: le nostre classi model dovranno implementare l’interfaccia Serializable, al fine di poter essere utilizzate tramite l’astrazione dei Repository fornita da Spring-Data. Approfondiremo questo tema nel paragrafo seguente, quando introdurremo appunto i Repository. Per ora ci limitiamo a far implementare la marker interface “Serializable” alla nostra classe base astratta “ABaseModel”.

Il codice completo della classe ABaseModel sarà quindi il seguente:

package net.davismol.springangulardemo.models; import java.io.Serializable; import org.springframework.data.annotation.Id; import lombok.Data; @Data public abstract class ABaseModel implements Serializable { private static final long serialVersionUID = 6139774281752467202L; @Id protected String id; }

Creiamo ora, sempre all’interno del package models, una sottoclasse concreta di ABaseModel per rappresentare le entità relative ai nostri utenti. Chiamiamo tale classe “User”.

Per il momento, definiamo all’interno della classe User le stesse variabili di istanza che avevamo definito per la classe UserViewModel, cioè per il nostro oggetto DTO (Data Transfer Object) da utilizzare per ritornare istanze di utenti alle view della nostra applicazione.

In realtà, generalmente, il DTO relativo ad una certa entità presenta un contenuto informativo diverso; può presentare, infatti, solo un subset delle variabili di istanza definite nel modello o dei campi in formato diverso dagli “originali”, per esigenze di esposizione in pagina.

In un secondo momento, introdurremo anche questo concetto nella nostra applicazione, diversificando le strutture dell’entità “User” e del relativo DTO “UserViewModel” per numero e tipo di campi presenti e vedremo dove e come realizzare le conversioni tra i due tipi.

Al solito, abbiamo utilizzato l’annotazione @Data di Lombok per la generazione dei getter e setter delle variabili di istanza aggiungendo, questa volta, l’annotazione @EqualsAndHashCode al fine di evitare un warning, dovuto alla mancata chiamata dei metodi equals() e hashcode() della superclasse, nell’implementazione, fornita da Lombok, di quelli della classe stessa.

@Data @EqualsAndHashCode (callSuper = true) public class User extends ABaseModel { private static final long serialVersionUID = -338470709597595899L; private String firstname; private String lastname; private String country; private int age; }

Se non specifichiamo nulla, gli oggetti della classe User saranno memorizzati su MongoDB all’interno di una collection con lo stesso nome della classe. Se vogliamo cambiare questo comportamento di default e assegnare alla collection un nome diverso, dobbiamo utilizzare l’annotation @Document, specificando tramite l’optional element “collection” il nome della collection desiderato.

Se, ad esempio, volessimo utilizzare il plurale per il nome della collection e quindi “users” invece di “user”, dovremmo aggiungere alla classe User la seguente annotazione:

@Document(collection="users")

Il codice completo della classe User, quindi, sarà:



CREAZIONE DEL REPOSITORY

Spring-Data ci mette a disposizione un meccanismo molto potente per le operazioni di interazione con il database sottostante: i Repository. I Repository sono delle astrazioni che ci permettono di minimizzare la quantità di codice da scrivere nel nostro Data Access Layer, offrendoci delle interfacce che implementano per noi out-of-the-box i metodi per il recupero e il salvataggio delle istanze delle nostre entità.

I Repository devono essere definiti a livello di singola entità gestita nel database, definendo quali sono la classe del model e il tipo del campo che rappresenta il suo id. La definizione dell’interfaccia Repository, che è una marker interface, è la seguente:

public interface Repository

Come detto, T è la classe che rappresenta le entità gestite da quel repository e ID è il tipo della sua proprietà identificativa.

Un’interfaccia che estende Repository è CrudRepository

public interface CrudRepository extends Repository

Essa fornisce la definizione dei metodi per l’esecuzione delle operazioni CRUD (Create, Read, Update, Delete) sul database, per una determinata entità. Di seguito la lista dei metodi esposti:

Nel nostro scenario, utilizziamo MongoDB come database e Spring-Data-MongoDB per implementare il Data Access Layer. In questo caso, l’interfaccia che ci viene fornita per definire il nostro repository è MongoRepository che è una estensione dell’interfaccia CrudRepository vista in precedenza. Anzi, in realtà MongoRepository estende PagingAndSortingRepository che a sua volta estende CrudRepository. PagingAndSortingRepository, lo dice il nome, aggiunge all’interfaccia i metodi per gestire l’ordinamento e la paginazione dei risultati.

Iterable findAll(Sort sort); Page findAll(Pageable pageable);

Dopo questa breve parentesi sui dettagli delle interfacce e sulla struttura della gerarchia, passiamo a vedere i Repository in azione.

Torniamo quindi al nostro progetto e creiamo un nuovo package che chiamiamo “repos”. Al suo interno creiamo l’interfaccia UserRepository che estende MongoRepository. I tipi da utilizzare nel Generics saranno quindi User, per definire la classe del model e String che è il tipo della proprietà Id della classe User stessa.

Quindi, l’interfaccia UserRepository sarà definita come segue:

@Repository public interface UserRepository extends MongoRepository { }

CREAZIONE DEL SERVICE

Ora che abbiamo creato il Repository per l’entità User come astrazione per l’interazione con il database per quanto riguarda le operazioni relative ad oggetti di questo tipo, possiamo passare alla creazione del Service che utilizzerà questo Repository per soddisfare le richieste provenienti dal Controller.

L’idea è infatti di mantenere un’architettura “pulita”, lasciando al Controller la sola gestione delle richieste http, al Repository la parte di accesso ai dati e di intermediare questi due componenti con l’utilizzo di un Service che appunto, recupera i dati tramite il Repository e li fornisce al Controller.

Aggiungiamo al nostro progetto un nuovo package che chiamiamo “services”. Al suo interno creiamo una classe che chiamiamo “UserService” e che annotiamo con @Service.

All’interno della classe definiamo un oggetto UserRepository che annotiamo con @Autowired in modo da lasciarne la gestione a Spring Framework. Per ora, la nostra applicazione offre un’unica funzionalità, quella di recuperare l’intera lista degli oggetti dell’entità User. Per soddisfare questa richiesta, definiamo nel nostro Service un metodo che si occuperà di effettuare questa operazione e di restituire il risultato al controller.

Dichiariamo quindi un metodo getAllUsers() che non necessita di un nessun parametro di input ma restituisce invece una lista di oggetti della classe UserViewModel, che è l’oggetto DTO che abbiamo definito per passare i dati alla pagina.

La signature del metodo sarà quindi la seguente:

public List getAllUsers()

La classe UserService, arrivati a questo punto, si presenta quindi come segue. Per il momento, al fine di renderla compilabile, inizializziamo la lista di UserViewModel a null.

@Service public class UserService { @Autowired UserRepository userRepo; public List getAllUsers() { List users = null; // TO BE IMPLEMENTED return users; } }



Il body del metodo dovrà eseguire le seguenti operazioni:

Interrogare il repository UserRepository per recuperare la lista di entità User

Convertire la lista di oggetti User in una lista di UserViewModel

Restituire la lista di UserViewModel al controller

Per il primo punto non dobbiamo fare altro che invocare il metodo findAll() fornito di default dal nostro UserRepository. Sfruttando l’intellisense di Spring Tool Suite possiamo vedere tutti i metodi offertici out of the box dal nostro UserRepository grazie all’implementazione dell’interfaccia Repository di Spring-Data.



Il metodo che serve a noi, abbiamo detto, è findAll()

List userList = userRepo.findAll();

Il Repository, essendo il nostro Data Access Layer, ci restituisce oggetti che rappresentano le entità di business della nostra applicazione. Nel Service layer, come abbiamo detto, dobbiamo occuparci di trasformare questi oggetti in oggetti che il controller dovrà restituire alle viste, ovvero i nostri ViewModel.

Vai alla parte successiva dell’articolo: Creare una web application con Spring Boot, MongoDB, Angular 4 e TypeScript e deployarla in cloud come Microsoft Azure Webapp – Parte 10