Hello,

J’aurais voulu avoir un avis sur une architecture que j’ai mise en place sur une application.

Voilà une brève description avant de vous montrez le code.

Elle est basée sur 3 couches :

-Présentation (JSF)
-Service (implémentation perso)
-Persistance (JDBC)

L’avis que j’aimerais avoir concerne les couches services et persistances.
Autant le dire tout de suite, les technos utilisées sur le projet sont très classiques (imposées par l’historique de l’application).

La couche Service est représentée par 3 classes:
Une interface
La classe d'implémentation de cette interface
une classe Factory permettant d'obtenir une instance de ma classe d'implémentation :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
 
public interface MonAppliServices {
 
 
    public List InternalSearchJob(InternalSearchParamsDTO param)throws MonAppliServiceException;
 
 
 
    public List CandidatSearchJob(SearchParamsDTO params)throws MonAppliServiceException;
 
//... Liste de mes services
 
}
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MonAppliServicesFactory {
 
 
 
    private static MonAppliServices monAppliServices = new MonAppliServicesImpl();
 
 
 
    public static MonAppliServices create(){
 
 
 
        return monAppliServices;
 
    }
 
}
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
 
 
 
 
 
    public static Logger log = Logger.getLogger(MonAppliServicesImpl.class);
 
 
 
    /**
 
     * Methode permettant d'effectuer une recherche interne sur les offres d'emploie
 
     * @param InternalSearchParamsDTO - ensemble des parametres de recherche
 
     *
 
     */
 
    public List InternalSearchJobOffer(InternalSearchParamsDTO params)throws MonAppliServiceException {
 
 
 
        MonAppliPersistance wp = MonAppliPersistanceFactory.create();
 
 
 
 
 
        List result=null;
 
        try {
 
            wp.initConnection();
 
            result = wp.internalSearchJobOffer(params);
 
        } catch (MonAppliPersistanceException e) {
 
            log.error("Une erreur c'est produite lors de la recherche interne",e);
 
            throw new MonAppliServiceException("Une erreur c'est produite lors de la recherche interne :"+e.getMessage() );
 
        }finally{
 
            try {
 
                wp.finishConnection();
 
            } catch (MonAppliPersistanceException er) {
 
                log.error("Une erreur c'est produite lors de la fermeture ",er);
 
                throw new MonAppliServiceException("Une erreur c'est produite lors de la fermeture :" +er.getMessage() );
 
            }
 
 
 
        }
 
        return result;
 
    }
 
 
 
 
 
    /**
 
     * Methode permettant d'effectuer une recherche candidat sur les offres d'emploie
 
     * @param InternalSearchParamsDTO - ensemble des parametres de recherche
 
     *
 
     */
 
    public List CandidatSearchJobOffer(CandidatSearchParamsDTO params)throws MonAppliServiceException {
 
 
 
        MonAppliPersistance wp = MonAppliPersistanceFactory.create();
 
 
 
 
 
        List result=null;
 
        try {
 
            wp.initConnection();
 
            result = wp.CandidatSearchJobOffer(params);
 
        } catch (MonAppliPersistanceException e) {
 
            log.error("Une erreur c'est produite lors de la recherche candidat ",e);
 
            throw new MonAppliServiceException("Une erreur c'est produite lors de la recherche candidat :"+e.getMessage() );
 
        }finally{
 
            try {
 
                wp.finishConnection();
 
            } catch (MonAppliPersistanceException er) {
 
                log.error("Une erreur c'est produite lors de la fermeture ",er);
 
                throw new MonAppliServiceException("Une erreur c'est produite lors de la fermeture :" +er.getMessage() );
 
            }
 
 
 
        }
 
        return result;
 
    }
Dans cette couche, on peut voir les appels à la couche persistantce :
Par exemple :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
 
 
    MonAppliPersistance wp = MonAppliPersistanceFactory.create(); (1)
 
            wp.initConnection();(2)
 
            result = wp.CandidatSearchJob (params);(3)
            wp.appelDunAutreTraitement() (4)
        ......
 
 
                wp.finishConnection();(4)
La ligne (1) récupère une instance de la couche persistance.
La ligne (2) initialise une connexion pour les traitements à effectuer sur la couche de persistance (depuis une datasource)
La ligne (3) effectue un traitement (dans notre cas il effectue une recherche).
La ligne (4) effectue un autre traitement.
La ligne (5) rend la connexion au pool.

Chaque service effectue les mêmes types d’appel pour interagir avec la couche de persistance. De cette manière, chaque service appelé par la couche de présentation n’utilise qu’une seule connexion.


Voilà maintenant la couche persistance, elle est basée sur le même principe que la couche de service, c’est à dire 3 classes :
Une interface, une classe d’implémentation, une classe Factory :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 
 
 
public interface MonAppliPersistance {
 
 
    public void initConnection() throws MonAppliPersistanceException;
 
 
    public void finishConnection()throws MonAppliPersistanceException;
 
    //Recherche JobOffer
    public List internalSearchJobOffer(InternalSearchParamsDTO  param)throws MonAppliPersistanceException;
 
    public List CandidatSearchJob (CandidatSearchParamsDTO param) throws MonAppliPersistanceException;
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
 
 
public class MonAppliPersistanceFactory {
 
    public static MonAppliPersistance create(){
 
 
        return  new MonAppliPersistanceImpl();
    }
}
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
 
 
 
public class MonAppliPersistanceImpl extends AbstractPersistanceManager implements MonAppliPersistance {
 
 
 
    private static Logger log = Logger.getLogger(MonAppliPersistanceImpl.class);
 
    private Connection connexion = null;
    private PreparedStatement pst = null;
    private ResultSet rs = null;
 
 
 
    /**
     * Methode permettant d'initialiser le contexte de connection
     * Cette methode doit Ítre appelee systematiquement avant l'utilisation de la classe 
     * persistance
     * @throws PersistanceAccessException
     */
    public void initConnection() throws MonAppliPersistanceException{
        try {
            connexion = getConnection();
        } catch (Exception e) {
            log.error("Impossible de rÈcuperer une connection");
            throw new WebCamPersistanceException("Impossible de rÈcuperer une connection");
        }
    }
 
    /**
     * Methode permettant d'initialiser le contexte de connection ‡ partir 
     * d'une connextion externe. Cette methode est essentiellement utilisÈe pour le framework JUnit
     * 
     * 
     * @throws PersistanceAccessException
     */
    public void initConnection(Connection _conn) throws MonAppliPersistanceException{
        try {
            connexion = _conn;
 
        } catch (Exception e) {
            log.error("Impossible de rÈcuperer une connection");
            throw new WebCamPersistanceException("Impossible de rÈcuperer une connection");
        }
    }
 
    /**
     * Methode permettant de rendre la connection a la base de donnees.
     * Cette methode doit etre systematique appelee dans une clause finally apres l'utilisation de la 
     * classe de persistance
     * @throws PersistanceAccessException
     */
    public void finishConnection()throws MonAppliPersistanceException{
        try {
            if(rs!=null)rs.close();
            if(pst!=null)pst.close();
            if(connexion!=null)connexion.close();
        } catch (SQLException e) {
            log.error("Erreur lors de la fermeture des connections",e);
            throw new MonAppliPersistanceException("Erreur lors de la fermeture des connections");
        }
    }
 
    /**
     * Methode permettant d'executer une recherche interne selon les parametres saisie
     * @param InternalSearchParamsDTO - parametres de la requete
     * @return List - ensemble des enregistrements trouvÈs
     */
    public List internalSearchJobOffer(InternalSearchParamsDTO param) throws MonAppliPersistanceException{ 
 
        String query = « SELECT * FROM TABLE » ;
 
        List r = new ArrayList();
        try {
            pst = connexion.prepareStatement(query);
            rs = pst.executeQuery();
            while(rs.next()){
                InternalSearchResultDTO isrdto = new InternalSearchResultDTO();
 
                isrdto.setLibPosition(rs.getString(1));
                isrdto.setDateModitication(rs.getDate(2));
                isrdto.setOwnerFirstName(rs.getString(3));
                isrdto.setOwnerLastName(rs.getString(4));
                isrdto.setPublisherFirstName(rs.getString(5));
                isrdto.setPublisherLastName(rs.getString(6));
                isrdto.setLibBusinessUnit(rs.getString(7));
                isrdto.setLibStatutOffer(rs.getString(8));
                isrdto.setIdOffer(rs.getBigDecimal(9));
 
                r.add(isrdto);
            }
 
 
        } catch (SQLException e) {
            log.error("Erreur lors de l'execution de la requete de recherche",e);
            throw new MonAppliPersistanceException("Erreur lors de l'execution de la requete de recherche : "+ query); 
        }
 
        return r;
    }
 
    /**
     * Methode permettant d'executer une recherche interne selon les parametres saisie
     * @param InternalSearchParamsDTO - parametres de la requete
     * @return List - ensemble des enregistrements trouvÈs
     */
    public List CandidatSearchJobOffer(CandidatSearchParamsDTO param) throws MonAppliPersistanceException{ 
 
        String query = « Select * from TABLE ;
        List r = new ArrayList();
        try {
            pst = connexion.prepareStatement(query);
            rs = pst.executeQuery();
            while(rs.next()){
                CandidatSearchResultDTO cisrdto = new CandidatSearchResultDTO();
 
                cisrdto.setLibPosition(rs.getString(1));
                cisrdto.setDateModitication(rs.getDate(2));
                cisrdto.setLibCountry(rs.getString(3));
                cisrdto.setLibRegion(rs.getString(4));
                cisrdto.setLibContract(rs.getString(5));
                cisrdto.setIdOffer(rs.getBigDecimal(6));
 
                r.add(cisrdto);
            }
 
 
        } catch (SQLException e) {
            log.error("Erreur lors de l'execution de la requete de recherche",e);
            throw new MonAppliPersistanceException("Erreur lors de l'execution de la requete de recherche : "+ query);
        }
 
        return r;
    }
//... Autres methodes d’acces aux données
Dans la classe d'implémentation de la couche persistance on trouve 3 variables d'instance : Connexion, PreparedStatement, resultSet.
Ces variables sont globales à toutes la classe et initialisées lorsque la méthode initConnection().
Toutes les méthodes ensuite appelées utilise la même connexion.
Lorsque l'accès aux données est terminé, alors la méthode finishConnection() est appelée et la connexion est refermée.

La connection est fournie par la classe abstraite AbstractPersistanceManager par l'intermédiaire de la méthode getConnection().

En mettant en place cette architecture , j'ai voulu faire la séparation des responsabilités : services fonctionnels et accès aux données.
Le passage d'informations entre les couches est assuré par des objets de type DTO.



J'envisage d'intégrer, à la place de mes classes factory, le framework spring et à terme de remplacer ma couche persistance par le framework Hibernate.
D'ailleurs je pense que le replacement pourra se faire sans douleur étant donnée la séparation mise en place (qu'en pensez vous?)

Voila, bon le post est assez dense.
En fait, ce que j'aurais souhaité c'est avoir un avis critique sur cette architecture.


Merci d'avance pour vos remarques!

Cordialement,