tutorials.de Buch-Aktion 05/2012
Like Tree2Danke
  • 1 Beitrag von Oliver Gierke
  • 1 Beitrag von Thomas Darimont
ERLEDIGT
NEIN
ANTWORTEN
3
ZUGRIFFE
3058
EMPFEHLEN
  • An Twitter übertragen
  • An Facebook übertragen
AUF DIESES THEMA
ANTWORTEN
  1. #1
    Avatar von torax13
    torax13 torax13 ist offline Mitglied Gold
    Registriert seit
    Jul 2007
    Beiträge
    155
    Hallo,

    nachdem ich die letzten Jahre zwar Webanwendungen entwickelt hab, dies aber mit einem hausinternen Framework, beschäftige ich mich im Moment mit 'aktuellen' (ich weiß, ein alter Hut für viele) Themen wie JPA und JSF.

    Zu den einzelnen Technologien existieren ja viele Anleitungen und Howtos für ausgewählte Beispiele (ich les gerade das 'Java Persistence with Hibernate' eine sehr gute Einführung in JPA). Was mir bisher fehlt ist ein Überblick wie ich ein Projekt mit JPA und JSF am besten strukturiere.

    Ich könnt zwar mein kleines Beispielprojekt sicherlich irgenwie 'zurechtfriemeln' aber mir gehts ja gerad darum eine gute Strukur herauszuarbeiten.

    Layeransicht:
    * Persistensklassen (das reine Mapping)
    * DAO (Kappselung der Persistensklassen in Bussiness Objekte, hier kommen auch die Regeln rein?)
    * View (JSF)

    Hier fehlt sicherlich noch die Controller Schicht, da ich bei JSF aber noch sehr am Anfang stehe, wüßt ich im Moment nicht so richtig, was dessen Aufgabe eigentlich ist.

    Wie trennt man das am besten in Packages?
    * org.torax.project.persistence (hier z.B. Sonne.java, Mond.java, Sterne.java)
    * org.torax.project.DAO (SonneDAO.java, MondDAO.java, SterneDAO.lava)
    * ..?

    Wo pack ich am besten die JUnit Test Klassen hin?

    Wahrscheinlich steh ich einfach nur etwas auf dem Schlauch, habe aber leider kein Beispielprojekt an dem ich mich orientieren kann.

    Für Hinweise und oder Links wäre ich sehr dankbar.

    Torax
     
    Falls ich entgegen meiner Gewohnheiten mal einen hilfreichen Beitrag schreibe, freu ich mich über eine positive Bewertung.

  2. #2
    Avatar von Oliver Gierke
    Oliver Gierke Oliver Gierke ist offline Mitglied Rubin
    Registriert seit
    Dec 2003
    Ort
    Mannheim
    Beiträge
    1.457
    Aaaalso. Grunsätzlich ist das enicht verkehrt was du da machst - allerdings sind ein paar kleine Denkfehler drin. Zum anderen ist dasauch immer etwas Geschmacksfrage.

    Zum kleinen Fehlerchen. Das was du als Persistenzklassen bezeichnest sind eigenlich Entities die den Kern deines Domänenmodells ausmachen. Daher würde ich das package eher foo.bar.domain nennen. DAO hast du auch etwas falsch verstanden (oder vielleicht nur komisch aufgeschrieben?). Die sind Repositories im Fowlerschen Sinne - sprich Laden und Speichern der Entities. D.h. diese kapseln Daten(bank)zugriffslogik, sollten allerdings keine Logik beinhalten.

    Nun kommen wir zum Thema Geschmackssache . Klassischerweise hat man eine Präsentationsschicht (JSF, SpringMVC, Struts whatever) die auf einen Servicelayer zugreift. In dem bildet man nette Komponenten: CustomerManager, AccountManager usw. Dieser layer orchestriert quasi Aufrufe an DAOs, Domänenobjekte usw. Hier steckt auch im Allgemeinen viel Geschäftslogik. Wichtig ist dabei auch, dass das Interface des Layers (also auch der einzelnen Teilkomponenten) stark an den Clientbedürfnissen ausgerichtet ist und eher Fachlich orientiert sein sollte. Sowas wie createAccount ist ganz nett z.B. DAOs haben schon recht technische Interfaces: findByUserId. Bei einfachen Webanwendungen macht der Servicelayer eigentlich nix anderes, als Aufrufe an DAOs weiterzureichen. Allerdings abstrahiert er dabei natürlich technische Details vom Client weg. Achso, die Domänenobjekte kannst du dann in diesem Modell durch alle Layer durchreichen, solang die Abhängigkeit unidirektional bleibt (also Presentation -> Domain, Service -> Domain, DAO -> Domain).

    Nachteil dieses Modells ist, dass die Domainklassen recht häufig wenig Verhalten aufweisen und dass geschäftslogik, die eigentlich mit Domainklassen verbunden sein sollte in Services liegt. Dem Arbeitet der Ansatz des Domain Driven Design etwas entgegen, dessen wirkliche praktische Umsetzung (Domänenklassen haben Zugriff auf DAOs z.B.) doch etwas advanced ist, bzw. noch in den Kinderschuhen steckt.

    Zum packageging: ich bin auf jeden Fall Freund des "Fachlichkeit vor Technik". D.h.

    foo.bar.account.dao
    foo.bar.account.domain
    foo.bar.account.service
    foo.bar.account.view

    foo.bar.customer.dao
    foo.bar.customer.domain
    foo.bar.customer.service
    foo.bar.customer.view

    Das hat den Vorteil, dass man fachliche einheitn unabhängig voneinander deployen kann. In diesen packages liegen dann eigentlich immer die Interfaces + evtl. exposedte Exceptions. Unter .dao und .service mach ich meist noch ein impl package und plazier da die Implementierungen, einfach um die Packagedependencies zu kappen.

    Hilfreich für eine Kontrolle der Abhängigkeiten sind Tools wie SonarJ oder JDepend. Damit kannst du überwachen, dass du keine Zyklen in die Architektu bekommst usw.

    JUnittestklassen hab ich meist in einem separaten src-Folder, darunter allerdings die packages genauso wie bei den Produktivsourcen. Auch aus Deploymentgründen.

    Reicht das als Anfangstipp? Wenn nicht... weiter fragen

    REINHAUN!
    torax13 bedankt sich. 

  3. #3
    Registriert seit
    Jun 2002
    Ort
    Saarbrücken (Saarland)
    Beiträge
    9.886
    Blog-Einträge
    29
    Hallo,


    Da ich meine Anwendungen immer so entwerfe, dass die einzelnen Schichten immer möglichst lose gekoppelt sind und man so einfach weitere Schichten austauschen / parallel hinzufügen kann (Statt web Präsentationsschicht, ein Eclipse Rich Client) achte ich immer darauf das die einzelnen Schichten möglichst keine zyklischen Abhängigkeiten zueinander haben.

    Ich würde das dann so machen:
    Präsentationsschicht (JSF, JSP; Servlets) de.tutorials.core.presentation... (hier Model View Controller, oder ein anderes Pattern für den Presentation Layer (MVP, PM))
    Geschäftslogikschicht (BusinessServices, Domain Classes) -> de.tutorials.core.services, de.tutorials.core.domain
    Persistenzschicht (Legacy System ResourceConnectors,DAO's / Repositories, DataAssembler (JDBC/Spring JDBC basiert zur Massendatenverarbeitung) ) -> de.tutorials.core.persistence, de.tutorials.core.persistence.dao

    (de.tutorials.core ist in meinem Beispiel das Basis package des OSGi Bundles)

    Für alle BusinessServices,DAO / Repositories 's, DataAssembler, Connectors gibts öffentliche interfaces. Diese liegen im direkten package:
    Bsp:
    de.tutorials.core.persistence.dao.IGenericDAO

    Implementierungen verstecke ich immer in einem darunterliegenden Internal package:
    Bsp:
    de.tutorials.core.persistence.dao.internal.GenericHibernateDAO

    So habe ich die möglichkeit den Klassenzugriff innerhalb der einzelnen Schichten besser zu steuern (per AspectJ, OSGi Exported / Imported Packages, Dynamic Import, etc.).

    Geschäftslogik hätte ich in meinen Services und / oder in meinen Domain Classes. Die mapping Informationen (hbm.xml) würde
    ich auch in dem Domain package ablegen. Konfigurieren würde ich meine Services / Domain Objects über Spring, mit AspectJ und

    @Configurable (
    http://www.tutorials.de/forum/java/2...nd-spring.html)

    Ansonsten verwende ich für große Webanwendungen eine "Serverside Eclipse" Komponentenmodell. Komponenten sind in diesem Sinne
    dann meine OSGi Bundles, die dann Services, Hibernate Mappings, Spring application contexts über extension points und extensions in das Gesamtsystem einbringen. Damit hat man dann einen hochflexiblen Erweiterungsmechanismus Wenn man dann noch die "Erweiterbarkeit" als AspectJ aspect definiert bleibt die Anwendungslogik auf komplett frei von irgendwelchen Plugin-Mechanismus-Interna...
    Siehe auch:
    http://www.tutorials.de/forum/java/2...ionpoints.html
    http://www.tutorials.de/forum/java/2...n-equinox.html
    http://www.tutorials.de/forum/java/2...ipse-fleh.html

    Weiterhin würde ich nicht notwendigerweise für jedes Domain Object ein eigenes DAO schreiben sondern ein generisches, von dem
    ich dann in Spezialfällen ableite.

    Beispiel:
    Code java:
    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
    
    /**
     * 
     */
    package de.tutorials.framework.persistence.dao;
     
    import java.io.Serializable;
    import java.util.List;
     
    /**
     * @author Thomas.Darimont
     * 
     */
    public interface IGenericDAO {
        /**
         * 
         * @param entity
         * @return
         */
        <TKey extends Serializable> TKey makePersistent(Object entity);
     
        /**
         * 
         * @param entity
         */
        void makeTransient(Object entity);
     
        /**
         * 
         * @param key
         * @return
         */
        <TEntity, TKey extends Serializable> TEntity getBy(Class<TEntity> entityClass, TKey key);
     
        /**
         * 
         * @return
         */
        <TEntity> List<TEntity> findAll(Class<TEntity> entityClass);
     
        /**
         * 
         * @param attributeName
         * @param attributeValue
         * @return
         */
        <TEntity> List<TEntity> findByAttribute(Class<TEntity> entityClass,
                String attributeName, Object attributeValue);
     
        /**
         * 
         * @param attributeNames
         * @param attributeValues
         * @return
         */
        <TEntity> List<TEntity> findByAttributes(Class<TEntity> entityClass,
                String[] attributeNames, Object[] attributeValues);
    }

    Implementierung:
    Code java:
    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
    
    /**
     * 
     */
    package de.tutorials.framework.persistence.dao.internal;
     
    import java.io.Serializable;
    import java.sql.SQLException;
    import java.util.List;
     
    import org.hibernate.Criteria;
    import org.hibernate.HibernateException;
    import org.hibernate.Session;
    import org.hibernate.criterion.Property;
    import org.springframework.orm.hibernate3.HibernateCallback;
    import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
     
    import de.tutorials.framework.persistence.dao.IGenericDAO;
     
    /**
     * @author Thomas.Darimont
     * 
     */
    public class GenericHibernateDAO extends HibernateDaoSupport implements
            IGenericDAO {
     
        @SuppressWarnings("unchecked")
        public <TEntity> List<TEntity> findAll(final Class<TEntity> entityClass) {
            return getHibernateTemplate().loadAll(entityClass);
        }
     
        @SuppressWarnings("unchecked")
        public <TEntity> List<TEntity> findByAttribute(
                final Class<TEntity> entityClass, final String attributeName,
                final Object attributeValue) {
            return getHibernateTemplate().executeFind(new HibernateCallback() {
                public Object doInHibernate(Session session)
                        throws HibernateException, SQLException {
                    return session.createCriteria(entityClass).add(
                            Property.forName(attributeName).eq(attributeValue))
                            .list();
                }
            });
        }
     
        @SuppressWarnings("unchecked")
        public <TEntity> List<TEntity> findByAttributes(
                final Class<TEntity> entityClass, final String[] attributeNames,
                final Object[] attributeValues) {
            return getHibernateTemplate().executeFind(new HibernateCallback() {
                public Object doInHibernate(Session session)
                        throws HibernateException, SQLException {
                    Criteria criteria = session.createCriteria(entityClass);
                    for (int i = 0; i < attributeNames.length; i++) {
                        criteria.add(Property.forName(attributeNames[i]).eq(
                                attributeValues[i]));
                    }
                    return criteria.list();
                }
            });
        }
     
        @SuppressWarnings("unchecked")
        public <TEntity, TKey extends Serializable> TEntity getBy(
                Class<TEntity> entityClass, TKey key) {
            return (TEntity) getHibernateTemplate().get(entityClass, key);
        }
     
        @SuppressWarnings("unchecked")
        public <TKey extends Serializable> TKey makePersistent(Object entity) {
            return (TKey) getHibernateTemplate().merge(entity);
        }
     
        public void makeTransient(Object entity) {
            getHibernateTemplate().delete(entity);
        }
    }

    verwendet wird das dann beispielsweise so:
    Code java:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
            IGenericDAO genericDao = new GenericHibernateDAO(); // normally injected via Spring...
     
            List<Bubu> bubus = genericDao.findAll(Bubu.class);
            List<Gaga> gagas = genericDao.findAll(Gaga.class);
     
            List<Bubu> bubus0 = genericDao.findByAttribute(Bubu.class, "aaa", 4711);
     
            List<Bubu> bubus1 = genericDao.findByAttributes(Bubu.class,
                    new String[] { "aaa", "bbb" }, new Object[] { 4711, 3421 });
            
            Long id = genericDao.makePersistent(new Bubu());
            
            Bubu bubu = genericDao.getBy(Bubu.class, 1L);

    Ansonsten nutze ich bei sowas exzessiv die Möglichkeiten des Zusammenspiels von Technologien wie Spring, AspectJ, Hibernate, iBatis,
    Terracotta, OSGi, Equinox etc.

    Gruß Tom
    -AbeAdapti- bedankt sich. 
    Java rocks!
    How to become a good Java Programmer?
    Does IT in Java and .Net
    The only valid measurement of code quality: WTFs / minute
    Blog
    Xing
    Twitter

  4. #4
    Avatar von torax13
    torax13 torax13 ist offline Mitglied Gold
    Registriert seit
    Jul 2007
    Beiträge
    155
    Danke Euch beiden für Eure ausführlichen Erleuterungen. Insbesondere die Vorschläge von MSProductions zielen so in die Richtung, die ich micr auch vorgestellt hab und räumen auch einige Unklarheiten meinerseits aus.

    Das soll nicht heißen, das ich deine Vorschläge schlecht finde Thomas, ich denke aber ich mach erstmal Schritt 1 bevor ich Schritt 2 mache. Wenn ich zuviele neue Sachen gleichzeitig versuche, lande ich definitiv auf der Nase. Ich werde aber später, mit mehr Erfahrung (hoffentlich) nochmal auf den Thread zurückkommen und dann sicherlich Hinweise daraus entnehmen können.

    Fachliche / Technische Trennung: guter Hinweis, ich automatisch angefangen das technologisch zu trennen. Bin mir nocht ganz sicher, was mir da besser gefällt.

    Werde nun erstmal anfagen ein bischen Refactoring zu betreiben. Ich komm bestimmt mit weiteren Fragen zurück

    Gruß Torax
     
    Falls ich entgegen meiner Gewohnheiten mal einen hilfreichen Beitrag schreibe, freu ich mich über eine positive Bewertung.

Ähnliche Themen

  1. Antworten: 1
    Letzter Beitrag: 11.06.10, 06:28
  2. Gruppensystem (Strukturierung)
    Von marvinlol im Forum PHP
    Antworten: 16
    Letzter Beitrag: 09.08.09, 13:09
  3. Frage zur Strukturierung/Aufbau von dynamischen Seiten
    Von Tiefschneetaucher im Forum PHP
    Antworten: 5
    Letzter Beitrag: 01.04.07, 01:10