IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Développement full stack d'une application web avec Angular 7 et Spring Boot 2,
Un tutoriel de Georges Kemayo

Le , par Georges KEMAYO

32PARTAGES

10  0 
Bonjour,

L'article a pour but de présenter la conception, l'architecture et le développement full stack d'une application web en s'appuyant sur les technologies Java, Spring Boot et Angular. Plusieurs concepts sont abordés et expliqués dans l'article par la mise en oeuvre d'un exemple concret d'une application.

https://gkemayo.developpez.com/tutor...spring-boot-2/

Qu'en pensez-vous ?

Retrouvez les meilleurs cours et tutoriels pour apprendre le développement Web avec Spring Boot

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de Georges KEMAYO
Membre chevronné https://www.developpez.com
Le 07/12/2019 à 21:09
Citation Envoyé par marc.collin Voir le message
bravo pour le tutoriel

en couplant tes entités, tu brises un peu le concept de package by feature architecture
il aurait été possible de passer par des id, sinon de copier les données et de passer par un événement pour les mettres à jour, suppression...
Bonjour Marc.collin,

Tu as parfaitement raison, la contrainte du package by feature vient vraiment des "ressources" qui sont partagées entre domaines. J'étais bien conscient que je n'ai pas fait un aboutissement de cette architecture dans l'article. Le but est dans un premier temps de sensibiliser le lecteur sur cette architecture qui apporte son lot de concepts, d'avantages et d'inconvénients. Si le lecteur s'y intéresse, il ira chercher plus d'infos.

Les solutions que l'on peut proposer pour adresser ce problème sont multiples, chacune possédant ces avantages et inconvénients. Pour la solution que tu proposes de copier les données et de d'utiliser l'event sourcing pour les mettre à jour, c'est bien, mais ça passe par une duplication de données et la mise en place de l'event sourcing pour mettre à jour chaque domaine. C'est complexe et impossible d'expliquer tout cela dans un seul et même article .

Je peux même aller plus loin en disant que l'on peut même mettre en place le pattern Capture Data Change couplé à Kafka qui se chargera de mettre à jour la/les tables de chaque domaine. Là encore c'est très complexe et trop de travail rien que pour respecter le principe du package by feature.

Il vaut mieux s'y investir sur tout ce que nous venons de citer lorsqu'on est vraiment en contexte Microservice ou une vraie architecture domaine driven.

D'autres son de cloche, disent de sortir exceptionnellement les "ressources" partagées (en veillant à ce que cela ne devienne pas un fourre tout) dans un package "common" afin que les différents domaines se les partagent. Ce n'est pas moins intelligible, après tout c'est une organisation dans un seul et unique projet.

Cordialement,
Georges
1  0 
Avatar de jeffray03
Membre chevronné https://www.developpez.com
Le 12/02/2020 à 12:44
Salut,
tu effectues sans doute une Insertion avec la meme clé primaire dans la table Categorie. As tu declaré cette clé Auto_increment ?

Eric
1  0 
Avatar de parchemal
Membre averti https://www.developpez.com
Le 29/03/2020 à 19:12
Bonjour et Bravo pour ce tutoriel,

Voici quelques remarques:
  • L'architecture que vous présentez me semble bonne, mais adaptée pour des applications ayant très peu de classes ou de fonctionnalités , qu'en dites-vous ?
  • Par contre faites attention à vos contrôleurs. Car il y a du code métier dans vos contrôleurs puisque vous n'exploitez pas bien les réponses HttpStatus et ResponseEntity


Voici un exemple de votre contrôleur avec du traitement métier

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
    @PostMapping("/addCustomer")
    public ResponseEntity<CustomerDTO> createNewCustomer(@RequestBody CustomerDTO customerDTORequest) {
        //, UriComponentsBuilder uriComponentBuilder
        Customer existingCustomer = customerService.findCustomerByEmail(customerDTORequest.getEmail());//pas besoin
        if (existingCustomer != null) { //pas besoin
            return new ResponseEntity<CustomerDTO>(HttpStatus.CONFLICT); // pas besoin, ce traitement doit être fait par le service qui gère la sauvegarde, il faut renvoyer directement l'exception depuis le service
        }
        Customer customerRequest = mapCustomerDTOToCustomer(customerDTORequest);
        customerRequest.setCreationDate(LocalDate.now());
        Customer customerResponse = customerService.saveCustomer(customerRequest);
        if (customerResponse != null) {//idem, pas besoin
            CustomerDTO customerDTO = mapCustomerToCustomerDTO(customerResponse);
            return new ResponseEntity<CustomerDTO>(customerDTO, HttpStatus.CREATED);
        }
        return new ResponseEntity<CustomerDTO>(HttpStatus.NOT_MODIFIED);
    }
Voici un exemple de solution pour le service que je propose (Classe de gestion des exceptions à créer CustomerResourceException):

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public Customer saveOrUpdateCustomer(Customer customer) throws CustomerResourceException{
	try{
		return customerRepository.save(customer);
	} catch(DataIntegrityViolationException ex){
		logger.error("customer non existant", ex);
		throw new CustomerResourceException("DuplicateValueError", "Un customer existe déjà avec le compte : "+customer.getLogin(), HttpStatus.CONFLICT);
	} catch (CustomerResourceException e) {
		logger.error("Utilisateur non existant", e);
		throw new CustomerResourceException("CustomerNotFound", "Aucun utilisateur avec l'identifiant: "+customer.getId(), HttpStatus.NOT_FOUND);
	} catch(Exception ex){
		logger.error("Erreur technique de création ou de mise à jour de l'utilisateur", ex);
		throw new CustomerResourceException("SaveOrUpdateUserError", "Erreur technique de création ou de mise à jour du customer: "+customer.getLogin(), HttpStatus.INTERNAL_SERVER_ERROR);
	}
}
Et voici ce que devient le contrôleur:

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    @PostMapping("/addCustomer")
    public ResponseEntity<CustomerDTO> createNewCustomer(@RequestBody CustomerDTO customerDTORequest) {
        //, UriComponentsBuilder uriComponentBuilder
       try{
            Customer customerRequest = mapCustomerDTOToCustomer(customerDTORequest);
            customerRequest.setCreationDate(LocalDate.now());
            Customer customerResponse = customerService.saveCustomer(customerRequest);
            CustomerDTO customerDTO = mapCustomerToCustomerDTO(customerResponse);
            return new ResponseEntity<CustomerDTO>(customerDTO, HttpStatus.CREATED);
       } catch (CustomerResourceException ex) {
         //Traitez vos erreurs ici par un log si nécessaire
        return new ResponseEntity<CustomerDTO>(ex.getHttpStatus()); //Ici tu gères à la fois plusieurs types d'erreurs comme HttpStatus.NOT_MODIFIED, HttpStatus.CONFLICT etc , au lieu de forcer la réponse à HttpStatus.NOT_MODIFIED uniquement
       }  catch (Exception  ex) {
         //Traiter les erreurs techniques ici par un log
       return new ResponseEntity<CustomerDTO>(HttpStatus.INTERNAL_SERVER_ERROR); // un cas typique d'erreur ici serait une erreur liée au mapping, ou autres erreurs non gérées par le service
       }
        
    }
Voici un exemple du constructeur de ton Exception

Code : Sélectionner tout
1
2
3
4
5
    public CustomerResourceException(String errorCode, String message, HttpStatus httpStatus) {
        super(message);
        this.errorCode = errorCode;
        this.httpStatus = httpStatus;
    }
Note: Penser aux getter/setter dans l'exception

Au final, pas besoin d'une requête supplémentaire customerService.findCustomerByEmail(xxx) dans le createNewCustomer car, en cas conflit, le service sait le code d'erreur à retourner

Bon courage !!!!
1  0 
Avatar de marc.collin
Membre émérite https://www.developpez.com
Le 14/11/2019 à 14:12
bravo pour le tutoriel

en couplant tes entités, tu brises un peu le concept de package by feature architecture
il aurait été possible de passer par des id, sinon de copier les données et de passer par un événèment pour les mettres à jour, suppression...
0  0 
Avatar de momjunior
Membre actif https://www.developpez.com
Le 21/11/2019 à 19:01
Bonjour

Très bon tutoriel, cependant je rencontre quelques problèmes lors de l'exécution du code source téléchargeable à la fin de l'article.

Après avoir installé les dépendances avec npm install, j'exécute ng serve --open. Et là je reçois le message d'erreur suivant:

Proxy config file D:\workspace-spring_tool_suite\library-ui-master\src\proxy.conf.json does not exist.
Error: Proxy config file D:\workspace-spring_tool_suite\library-ui-master\src\proxy.conf.json does not exist.
Je ne sais pas si c'est une erreur de votre part, mais il a fallu que je change le chemin menant vers proxy.conf.json dans le fichier angular.json pour régler le problème:
Code : Sélectionner tout
1
2
"proxyConfig": "proxy.conf.json"
au lieu de:
Code : Sélectionner tout
1
2
"proxyConfig": "src/proxy.conf.json"
Ensuite lorsque j'accède à la page book-page, je reçois le message d'erreur suivant:

Code : Sélectionner tout
An error occurs when retreiving categories data
Ci-dessous une capture d'écran:



Voici l'erreur au niveau du log:

Error occurred while trying to proxy request /library/rest/category/api/allCategories from localhost:4200 to http://localhost:8082 (ECONNREFUSED) (https
://nodejs.org/api/errors.html#errors_common_system_errors)
Merci
0  0 
Avatar de momjunior
Membre actif https://www.developpez.com
Le 22/11/2019 à 11:02
finalement ça marche.

Je lançais le projet via l'option Spring Boot App, mais j'ai par la suite opté de le lancer via le server tomcat installé, tout en changer le port 8080 en 8082, et ça a marché.
0  0 
Avatar de DarkChyper
Candidat au Club https://www.developpez.com
Le 27/11/2019 à 14:46
Bonjour,

Le tuto est vraiment bien fait.

J'ai juste un petit soucis avec la base de données H2. J'ai fait toute la partie back et j'ai voulu tester.
Au premier démarrage de tomcat, pas de soucis.
J'ai fais des modifications et depuis impossible de relancer l'application :

Code : Sélectionner tout
1
2
3
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Violation d'index unique ou clé primaire: "PUBLIC.PRIMARY_KEY_3 ON PUBLIC.CATEGORY(CODE) VALUES 1"
Unique index or primary key violation: "PUBLIC.PRIMARY_KEY_3 ON PUBLIC.CATEGORY(CODE) VALUES 1"; SQL statement:
insert into category values ('INF', 'Informatique') [23505-200]
Je n'arrive pas à drop les données de la table category :/ j'ai essayé d'ajouté la commande
Code : Sélectionner tout
truncate table category;
dans le fichier sql chargé au début mais cela me retourne une exception ..

Edit : j'ai augmenté le cache de la log et je vois qu'en fait le soucis provient d'un drop qui ne se fait pas sur mes tables à cause de FK qui ne sont pas supprimées...
Code : Sélectionner tout
1
2
Impossible de supprimer "BOOK" car "FK88C0YDLO57PCGP137TNTRGQX1" dépend de lui
Cannot drop "BOOK" because "FK88C0YDLO57PCGP137TNTRGQX1" depends on it; SQL statement: drop table book if exists [90107-200]
0  0 
Avatar de Georges KEMAYO
Membre chevronné https://www.developpez.com
Le 07/12/2019 à 21:11
Citation Envoyé par momjunior Voir le message
finalement ça marche.

Je lançais le projet via l'option Spring Boot App, mais j'ai par la suite opté de le lancer via le server tomcat installé, tout en changer le port 8080 en 8082, et ça a marché.
0  0 
Avatar de Georges KEMAYO
Membre chevronné https://www.developpez.com
Le 07/12/2019 à 21:15
Citation Envoyé par DarkChyper Voir le message
Bonjour,

Le tuto est vraiment bien fait.

J'ai juste un petit soucis avec la base de données H2. J'ai fait toute la partie back et j'ai voulu tester.
Au premier démarrage de tomcat, pas de soucis.
J'ai fais des modifications et depuis impossible de relancer l'application :

Code : Sélectionner tout
1
2
3
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Violation d'index unique ou clé primaire: "PUBLIC.PRIMARY_KEY_3 ON PUBLIC.CATEGORY(CODE) VALUES 1"
Unique index or primary key violation: "PUBLIC.PRIMARY_KEY_3 ON PUBLIC.CATEGORY(CODE) VALUES 1"; SQL statement:
insert into category values ('INF', 'Informatique') [23505-200]
Je n'arrive pas à drop les données de la table category :/ j'ai essayé d'ajouté la commande
Code : Sélectionner tout
truncate table category;
dans le fichier sql chargé au début mais cela me retourne une exception ..

Edit : j'ai augmenté le cache de la log et je vois qu'en fait le soucis provient d'un drop qui ne se fait pas sur mes tables à cause de FK qui ne sont pas supprimées...
Code : Sélectionner tout
1
2
Impossible de supprimer "BOOK" car "FK88C0YDLO57PCGP137TNTRGQX1" dépend de lui
Cannot drop "BOOK" because "FK88C0YDLO57PCGP137TNTRGQX1" depends on it; SQL statement: drop table book if exists [90107-200]

Salut DarkChyper,

Normalement cela ne devrait pas poser de problème si tu arrêtais toute l'application via ton Tomcat en faisant un shutdown en invite de commande dans le dossier bin.
Puis en redémarrant avec start. Puisque l'appli utilise une base embarquée, tout devrait se réinitialiser au redemarrage.

Assures toi juste qu'aucun processus lié ne tourne au préalable sur ta machine.

Cordialement,
Georges
0  0 
Avatar de thierryler
Rédacteur https://www.developpez.com
Le 06/01/2020 à 11:00
Bravo et merci pour cet excellent article
0  0