Wiki Markup |
---|
SOA-arkkitehtuurissa palvelut ovat tilattomia ja proseduraalisia, joukko operaatioita ja niihin liittyviä rakenteitä. Lähestymistapa on siten vastakohta Domain driven \-tyyppiselle ohjelmoinnille, jossa oliot ovat tilallisia ja huolehtivat itse operaatioista. |
...
h2. 2.4.1 Rajapintoihin liittyvät ongelmat |
...
h3. Sisäisten rakenteiden näkyminen rajapinnassa |
...
Palvelurajapinnat ovat julkisia ja niihin liittyy useampia käyttöliittymiä tai integraatioita. SOA-arkkitehtuurissa täytyy huolehtia siitä, etteivät tietokantaan tehtävät muutokset heijastu rajapintaan ja riko siihen yhteydessä olevia käyttöliittymiä. Jos esimerkiksi tietojen tallennukseen on käytetty JPA (Hibernate) tekniikkaa, on huolehdittava siitä, ettei tietokantatauluja kuvaavia entity-olioita ole käytetty julkaistussa rajapinnassa parametreinä. Jos näin on tehty, rajapinta ei voi olla muuttumaton (tai ainakin erittäin vaikea pitää sellaisena), eikä ratkaisu tuo oikeastaan mitään lisäarvoa siihen että oltaisiin suoraan yhteydessä tietokantaan JDBC:n avulla. Tämä ongelma korostuu SOA-ratkaisussa, jossa lähtökohtaisesti rajapinnat ovat kenen tahansa muun palvelun käytettävissä (ei välttämättä ole edes saman toimittajan tekemiä). |
...
h3. Taaksepäin yhteensopivuus |
...
Jossain palvelun kehityskaaren vaiheessa voi tulla tarvetta muuttaa julkaistua rajapintaa. Esimerkiksi halutaan tehdä uusi toiminto, jossa toimintalogiikkaa tulee muuttaa tai jokin tietokantakenttä poistetaan käytöstä. Tällöin on huolehdittava siitä, että rajapintaan vasten olevat käyttöliittymät ja integraatiot eivät rikkoudu ja että niiden päivittäminen voidaan tehdä hallitusti. J2EE:ssä ei ole mahdollisuutta versioida pakkauksia siten, että niitä voitaisiin ajaa samanaikaisesti muuten kuin pakkausten nimeä muuttamalla. |
...
h2. 2.4.2 Peppi-arkkitehtuurissa käytettävä ratkaisu rajapintojen tekemiseen |
...
h3. Sisäisten rakenteiden näkyminen rajapinnassa |
...
Peppi-projektin yhteydessä on tehty Kuali-moduulien selvitystyötä sekä havaittu tiettyjen Kualissa käytettävien ratkaisumallien olevan hyödyllisiä Peppi-arkkitehtuurissa. Seuraavaksi on käyty esimerkinomaisesti läpi Kuali Student \-moduulin rajapintojen toteutus sekä arvioitu ratkaisumalleja käytettäväksi Peppi-arkkitehtuurissa |
...
. h4. Kuali Student \-projektin rajapintojen toteutus |
...
Rajapinta
_Rajapinta_ LuService-rajapinta sijaitsee *ks-lum-api/src/main/java* kansiossa *org.kuali.student.lum.lu.service* pakkauksessa. Rajapinta huolehtii learning unit \-olioihin, esimerkiksi opintojakso, liittyvistä toiminnoista. |
...
Rajapinta sisältää esimerkiksi seuraavan opintojakson perustiedot palauttavan metodin. |
...
Code Block |
---|
{code} /** * Retrieves core information about a CLU * @param cluId identifier of the CLU * @return information about a CLU * @throws DoesNotExistException cluId not found * @throws InvalidParameterException invalid cluId * @throws MissingParameterException missing cluId * @throws OperationFailedException unable to complete request */ public CluInfo getClu(@WebParam(name="cluId")String cluId) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException; {code} Metodi palauttaa DTO-olion(*org.kuali.student.lum.lu.dto.CluInfo*), joka sijaitsee *ks-lum-api/src/main/java* kansiossa *org.kuali.student.lum.lu.dto* pakkauksessa. |
...
Toteutus
_Toteutus_ Clu-luokka (canonical learning unit, joka kuvaa esimerkiksi opintojaksoa) on JPA-tekniikan avulla tehty rakenneluokka, jonka avulla tiedot voidaan tallentaa tietokantaan. Se sijaitsee *ks-lum-impl/src/main/java* kansiossa *org.kuali.student.lum.lu.entity* pakkauksessa. |
...
LuServiceImpl toteuttaa LuService-rajapinnan ja sijaitsee *org.kuali.student.lum.lu.service.impl pakkauksessa.* LuServiceImpl luokka sisältää myös JAX-WS annotaatiot, jotta samaa luokkaa käytetään webservice-endpoint luokkana. |
...
{code |
}
@WebService(endpointInterface = "org.kuali.student.lum.lu.service.LuService", serviceName = "LuService",
portName = "LuService", targetNamespace = "http://student.kuali.org/wsdl/lu")
@Transactional(noRollbackFor={DoesNotExistException.class},rollbackFor={Throwable.class})
public class LuServiceImpl implements LuService {
|
...
{code} Rajapinnassa käytettävät rakenteet on eristetty DTO-olion avulla persistoitavasta Clu-oliosta siten että käytetään LuServiceAssembler luokkaa muuttamaan moduulin sisäinen Clu-olio rajapinnassa käytettäväksi CluInfo-olioksi. |
...
getClu metodin toteutus: |
...
Code Block |
---|
{code} @Override public CluInfo getClu(String cluId) throws DoesNotExistException, InvalidParameterException, MissingParameterException, OperationFailedException { checkForMissingParameter(cluId, "cluId"); Clu clu = luDao.fetch(Clu.class, cluId); return LuServiceAssembler.toCluInfo(clu); } {code} Assembler muuttaa olion toiseksi toCluInfo\- metodin avulla |
...
{code |
} public static CluInfo toCluInfo(Clu entity) { if (entity == null) { return null; } CluInfo dto = new CluInfo(); // copy all simple fields - exclude complex data types BeanUtils.copyProperties(entity, dto, new String[] { "officialIdentifier", "alternateIdentifiers", "descr", "participatingOrgs", "primaryInstructor", "instructors", "stdDuration", "luCodes", "credit", "offeredAtpTypes", "fee", "accounting", "intensity", "campusLocationList", "accreditationList", "adminOrgs", "attributes", "metaInfo", "versionInfo" }); dto.setOfficialIdentifier(toCluIdentifierInfo(entity .getOfficialIdentifier())); dto.setAlternateIdentifiers(toCluIdentifierInfos(entity .getAlternateIdentifiers())); dto.setDescr(toRichTextInfo(entity.getDescr())); // accreditingOrg Deprecated in v 1.0-rc2 Replaced by Primary and // Alternate admin orgs dto.setAccreditations(toAccreditationInfos(entity.getAccreditations())); dto.setAdminOrgs(toCluAdminOrgInfos(entity .getAdminOrgs())); dto.setPrimaryInstructor(toCluInstructorInfo(entity .getPrimaryInstructor())); dto.setInstructors(toCluInstructorInfos(entity.getInstructors())); dto.setStdDuration(toTimeAmountInfo(entity.getStdDuration())); dto.setLuCodes(toLuCodeInfos(entity.getLuCodes())); if (entity.getOfferedAtpTypes() != null) { List<String> offeredAtpTypes = new ArrayList<String>(entity .getOfferedAtpTypes().size()); for (CluAtpTypeKey key : entity.getOfferedAtpTypes()) { offeredAtpTypes.add(key.getAtpTypeKey()); } dto.setOfferedAtpTypes(offeredAtpTypes); } dto.setFeeInfo(toCluFeeInfo(entity.getFee())); dto.setAccountingInfo(toCluAccountingInfo(entity.getAccounting())); dto.setAttributes(toAttributeMap(entity.getAttributes())); dto.setMetaInfo(toMetaInfo(entity.getMeta(), entity.getVersionNumber())); dto.setType(entity.getLuType().getId()); if (entity.getCampusLocations() != null) { List<String> campusLocations = new ArrayList<String>(entity .getCampusLocations().size()); for (CluCampusLocation cluCamp : entity.getCampusLocations()) { campusLocations.add(cluCamp.getCampusLocation()); } dto.setCampusLocations(campusLocations); } dto.setIntensity(toAmountInfo(entity.getIntensity())); dto.setVersionInfo(toVersionInfo(entity.getVersion())); return dto; } {code} Kuten tästä metodista voi nähdä entity-olio ja rajapinnassa julkaistu Dta-olio sisältävät samat attribuutit ja operaatio tuntuu tarpeettoman monimutkaiselta. Tämä on kuitenkin keino SOA-arkkitehtuurissa huolehtia siitä, että julkaistu rajapinta ei muuttuisi. Toteutus käyttää Apachen BeanUtils-kirjastoa helpottamaan tietojen kopiointia dto-olioon. |
...
h4. Yhteenveto Kuali Student \-projetissa käytettävälle ratkaisulle |
...
Hyötyjä DTO-suunnittelumallissa ovat: |
...
* Julkaistu rajapinta saadaan eristettyä moduulin sisäisitä rakenteista ja palvelun kehittämiselle jää pelivaraa |
...
* Voidaan optimoida julkaistua rajapintaa, kaikki tallennettuja tietoja ei välttämättä tarvitse siirtää |
...
* Voidaan tehdä toinen "näkymä", toinen julkinen rajapinta, joka käyttää eri dto-olioita |
...
Heikkouksia: |
...
* Lisää palvelun monimutkaisuutta ja tietoja täytyy muuttaa useampaan paikkaan. |
...
h3. Peppi-arkkitehtuurissa käytettävä ratkaisu |
...
Ratkaisumallina noudatetaan Kuali Student-projektissa esitettyä tapaa, *rajapinnat eristetään moduulin sisäisistä rakenteista DTO-suunnittelumallin avulla*. |
...
Rajapinnat tehdään omiin pakkauksiin moduulikohtaisesti. |
...
Dto-olioiden rakentamisessa hyödynnetään BeanUtils-kirjastoa kuten Kuali student \-projektissa. Toinen tapa rakentaa Dto-oliot on käyttää Builder-mallia. Tässä mallissa Dto-luokka sisältää staattisen Builder-luokan joka palauttaa Dto-olion. |
...
{code |
}
public class CourseunitDto implements Serializable {
private String name; //setters and getters
private CourseunitDto() {}
public static class Builder {
private CourseunitDto courseunitDto;
Builder() {
this.courseunitDto = new CourseunitDto();
}
public Builder name(String name) {
if (name == null) {
throw new IllegalArgumentException("Course should have a name");
}
this.courseunitDto.setName(name);
return this;
}
public CourseunitDto build() {
return this.courseunitDto; }
}
}
|
...
{code} {color:#000000}Tällöin dto-olio voidaan rakentaa seuraavasti. |
...
Code Block |
---|
{color}
{code}
new CourseunitDto.Builder.name("test").build();
|
Taaksepäin yhteensopivuus
...
{code} h2. Taaksepäin yhteensopivuus Käytetään OSGI-teknologiaa tehdään palvelut OSGI-bundleina, joita ajetaan Servicemix4-alustassa. OSGI mahdollistaa jar-pakkauksen(OSGI-bundle) eri versioiden ajamisen rinnakkain. |