16. Centrale Referentie Systemen
A-profiel (user app)
meldingene-loket notification redactie ... helpcenter
cart
(betalen)
search stadsplan
burger
API’sWork in progress:
17. ■API’s & SDK: developer portal
■Challenge: zero downtime deployment
■Reusable backend “engines”
-> Antwerp City Platform as a Service (AcPaaS)
-> Check http://antwerpen.digipolis.be!!!
Work in progress...
26. Tips
■ automate everything from day 1
■ use vagrant with production cookbooks
■ create a skeleton with separate
packages(npm,bower,NuGet,composer)
■ version your api’s/packages with semver
■ code review with pull requests
■ Write tests
■ separation by isolation #(micro)services
■ no agile excuses!
39. app.put('/srv/overzicht/d/items/:itemId/favorite', function(req, res) {
var overviewItemId = req.params.itemId;
var favorite = req.body.favorite;
app.astad.acl.requireLogin(app, req, res, function() {
var user = req.adata.user;
var context = users.hasContext(req, "employee") ? "employee" : "user";
favorites.favoriteItem(user, context, overviewItemId, favorite,
function(err, data) {
if (err) {
return res.status(500).json(getError(err.message));
} else if (data == null) {
return res.status(404).json(getError('Kon favoriet niet vinden.'));
} else {
return res.json(getResponse(data));
}
});
}, function() {
return res.status(401).json(getNoPermission());
});
});
40. app.put('/srv/overzicht/d/items/:itemId/favorite',
access.canFavorite(app),
function(req, res) {
var overviewItemId = req.params.itemId;
var favorite = req.body.favorite;
var user = req.adata.user;
var context = users.hasContext(req, "employee") ? "employee" : "user";
favorites.favoriteItem(user, context, overviewItemId, favorite,
function(err, data) {
if (err) {
return res.status(500).json(getError(err.message));
} else if (data == null) {
return res.status(404).json(getError('Kon favoriet niet vinden.'));
} else {
return res.json(getResponse(data));
});
});
41. /**
* Save an item as favorite
*/
function favoriteItem(user, context, overviewItemId, favorite, callback) {
var userId = user.id;
async.waterfall([
function getOverviewObject(cb) {
OverviewItem.findById(overviewItemId, cb);
},
function userHasFavorites(overviewObject, cb) {
if (overviewObject == null) {
return cb(new Error('OverviewItem bestaat niet.'));
}
Favorite.findOne({userId: userId, context: context}, cb);
},
function createFavoritesObjectIfNeeded(favoriteObject, cb) {
if (favoriteObject == null) {
if (favorite) {
// Create favorite object for user
return saveFavorite(userId, context, overviewItemId, function saved(err, data) {
return favoriteSaved(err, data, overviewItemId, cb);
});
} else {
return cb(new Error("Dit item behoort nog niet tot je favorieten."));
}
} else {
var foundId = favoriteObject.overviewItemIds.indexOf(overviewItemId) !== -1;
if (foundId) {
if (favorite) {
return cb(new Error("Je hebt dit item al toegevoegd aan je favorieten."));
} else {
favoriteObject.overviewItemIds.pull(mongoose.Types.ObjectId(overviewItemId));
return favoriteObject.save(function saved(err, data) {
return favoriteSaved(err, data, overviewItemId, cb);
});
}
} else {
if (favorite) {
favoriteObject.overviewItemIds.push(mongoose.Types.ObjectId(overviewItemId));
return favoriteObject.save(function saved(err, data) {
return favoriteSaved(err, data, overviewItemId, cb);
});
} else {
return cb(new Error("Je moet dit item toevoegen aan je favorieten voordat je het kan verwijderen uit je favorieten."));
}
}
}
}
], callback);
}
42. /**
* Save an item as favorite
*/
function favoriteItem(user, context, overviewItemId, favorite, callback) {
async.waterfall([
function getOverviewObject(cb) {},
function userHasFavorites(overviewObject, cb) {},
function createFavoritesObjectIfNeeded(favoriteObject, cb) {}
], callback);
}
88. ■API: styling API with variables, mixins,
functions
■Base: styling for base HTML elements
■Grid: the Singularity grid
■Helpers: definition of helper classes
Sass-kit
89. ■Components:
●Collections of HTML elements that
form a new element together
●Directives
■Vendor: styling overrides for vendor
components
Sass-kit
Situatie van een paar jaar terug toen er nog geen sprake was van het A-stad platform
Applicatielandschap: oplossingsarsenaal van Digipolis bestaat uit een waaier aan technologieën: (home made in .Net, aangekochte pakketten. Recent meer SaaS toepassingen)
Gelinkt met CRS’en om authentieke brondata op te halen (bv. persoons- of ondernemingsgegevens)
Elke technologie heeft zijn eigen user interface + UX. Waaier aan toepassingen en websites waar de gebruiker zijn weg moet in zoeken (zowel burgers als medewerkers van de Groep Antwerpen) => digitale versnippering
Vooraleer er code kon getypt worden, moest er eerst draagvlak gecreëerd worden om iets te doen aan die digitale versnippering. Op zich een lang verhaal, kort samengevat:
in 2012 werd er naar aanleiding van een opleiding een visietekst goedgekeurd door alle bedrijfsdirecteurs van de Stad. Hierin stonden een aantal belangrijke standpunten rond hoe we met klanten omgaan: We willen streven naar 1 profiel, ipv dat de klant zich voor verschillende diensten van de Stad steeds opnieuw moeten registreren + waarom moet een gebruiker altijd opnieuw gegevens alle gegevens invullen als hij een product aanvraagt.
volgend wapenfeit was een stadsbrede digitale strategie dat het bedrijf Ondernemen en Stadsmarketing heeft uitgeschreven. Bevat een aantal belangrijke principes die we nog steeds in ons achterhoofd houden als we nieuwe oplossingen uitwerken in A-stad. Bv. de gebruiker staat centraal en verkrijgt relevante digitale communicatie door personalisering. Dit alles is vervolgens in een collegebesluit gegoten.
Bedrijf personeelsmanagement was op zoek naar een remedie voor de digitale fragmentering door een portaal te creëren specifiek voor medewerkers en wou dus ook mee op de kar springen.
Ondertussen zijn zo goed als alle bedrijven betrokken, wat niet meer dan normaal is voor een stadsbreed platform.
in het begin in het betty kot op Den Bell waar de meeste collega’s van de Stad werken, nadien verhuis naar Digipolis
Screenshot van één van de eerste versies van A-stad. Toont de visie waar we naartoe willen werken: verzameling apps die de ingelogde gebruiker kan favoriten of installeren, net zoals in de gekende app stores. Dit maakt van A-stad een platform (en niet enkel een website), en moet het op termijn mogelijk maken dat externe partijen apps gaan ontwikkelen.
Eén coherente, responsive user interface. Conceptueel is het de bedoeling dat we de zwarte balk bovenaan laten terugkomen boven alle apps of websites van Antwerpen. Je kan daar inloggen, zoeken, notificaties raadplegen en naar het overzicht gaan om content of apps te favoriten (=> personalisatie). Content groeperen we via kanalen, dat zijn eigenlijk mini sites met een bepaald thema.
de user app staat centraal omdat zo goed als alle apps daarmee communiceren. Dit zijn een paar voorbeelden van apps (in totaal hebben we er een stuk of 30). Ook tussen apps onderling is er veel communicatie. We streven er naar om apps zo veel autonoom te laten draaien en onafhankelijk van elkaar te deployen. backlog (van alle bedrijven) is heel groot, daarnaast moeten er voor bepaalde apps nog een nieuwe fundering gegoten worden om die backlog te kunnen realiseren. Dus elke sprint is een evenwichtsoefening tussen nieuw functionaliteiten en refactoring.
basisprincipe: “Alles is verbonden met elkaar” -> apps connecteren meestal niet rechtstreeks naar de backend toepassing, maar via de ESB. Vervult meerdere functies:
de consumer van de koppeling is veel minder afhankelijk van de backend toepassing.
bus kan data transformeren
bus kan data orchestreren en verschillende services aanspreken om een gecombineerd resultaat te bouwen.
voordeel van de bus wordt groter als er meerdere afnemers zijn van een service => hergebruik
issues:
in het begin werkte het busteam onafhankelijk van het A-stad team => API’s werkten niet zoals we verwachten. Nu zit er ene buschauffeur mee in het scrumteam en kan er veel korter op de bal gespeeld worden.
daarnaast hebben we wel dikwijls problemen met performantie van API’s. Aan services die we zelf geschreven hebben kunnen we sleutelen, maar bij aangekochte pakketten is dat al lastiger en bij SaaS oplosssingen wordt het helemaal moeilijk. Dus moeten we dikwijls via caching performantieproblemen oplossen.
in de weken voor de grote release: moment van totale ontreddering bij Jo Giraerts, één van onze lead backend developers
Maar na realisatie mag het resultaat er wel zijn: meldingsapp. In deze app kan een gebruiker iets melden aan de stadsdiensten (sluikstort of een losse straatsteen bv.). Ook medewerkers kunnen meldingen maken, bv. voor ICT support of aan de personeelsdienst.
Koppelt momenteel achterliggend met 3 backoffice toepassingen.
vroeger: veel verschillende meldingsformulieren met hun eigen layout, waarbij je iedere keer je persoonlijke gegevens moest ingeven.
nu: één drop down met alle keuzes (mobile first design). Vervolgens krijg je een form die er altijd ongeveer hetzelfde uit ziet, aangezien dit gekoppeld is met uw profiel zijn heel wat velden geprefilled.
heel het e-loket werkt volgens dit principe.
Om het A-stad platform te realiseren hebben we heel wat nieuwe technologieën in huis gehaald, Dries zal subiet een overzicht geven en sommige zitten ook achter de bus, zoals Solr. We gebruiken Solr
voor de algemene search (zwarte balk)
voor in app searches (via facetten)
om data op te halen voor bepaalde apps (bv. gerelateerde content in stadsplan)
Elke app is verantwoordelijk om via de ESB de data naar Solr te sturen. Soms wordt die data dynamisch verstuurd (bij een Save), soms zijn het cron jobs die daily of hourly draaien.
Medewerkersluik: van het moment dat ik mij identificeer met mijn medewerkerscredentials => verandert de kleur van het portaal, krijg ik andere content en apps ter beschikking. Draait op dezelfde infrastructuur en technologie als het burgerluik -> Logische keuze: veel apps komen terug.
Elke app voorzien van een API en samen met een SDK publiceren op een developer portal zodat externen ook apps kunnen bouwen op het platform (Uiteraard rekening houdend met de privacy wetgeving). We willen hierbij aansluiting zoeken bij o.a. start ups en een community uitbouwen
Naast de stad zijn ook politie en het OCMW elk een user centric portaal aan het bouwen, met gelijkaardige noden. Het kan niet de bedoeling zijn om telkens opnieuw een variant van eenzelfde component te bouwen of aankopen. Vandaar dat we beroep willen doen op start ups om deze gemeenschappelijke componenten mee te ontwikkelen. Dat is de basis van het Antwerp City Platform as a Service
Ik zou kort even de highlevel architectuur willen overlopen. Ik denk niet dat het een traditionele overheids stack is, toch zeker niet toen we met het project begonnen. Het is een vrij service georienteerde architectuur volledig gebaseerd op opensource componenten.
Het idee toen we aan de applicatie starten was om 1 single page app te maken waar alle toekomstige burger en medewerkers apps op gemaakt zouden worden.
Op zich hebben we 2 soorten consumers, de externe applicaties die via de API manager connecteren en onze astad applicatie die via antwerpen.be ontsloten is. Zo is er bijvoorbeeld een freewifi project dat gratis wifi aanbied aan iedereen met een aprofiel op de meir.
De Astad applicaties draaien op zich volledig geïsoleerd draaien. Iedere applicatie heeft zijn eigen frontend, backend en database. Het enigste dat geshared wordt in de Astad stack is een Redis instance voor de sessies.
Zo goed als alle externe systemen roepen we op via de ESB. Meldingen worden bijvoorbeeld doorgestuurd naar SAP of de kalender applicatie wordt gevoed door onder andere de UIT database.
voorbeelden
We hebben op zich een 35 applicaties die zich beperken tot 1 functioneel domein.
Alle assets van iedere applicatie zitten bijvoorbeeld in de assets app in gridfs. In deze app kan je images uploaden die al dan niet private zijn, images resizen en worden ze automatisch gescanned op virussen.
Een andere belangrijke app waar we het nog niet over gehad hebben is onze redactie omgeving die integreert met onder andere kanalen,stadsplan,kalender. De redacteur kan binnen deze app kiezen in welk kanaal hij de content will publiceren of kan zijn content linken aan gegevens uit het stadsplan of de kalender
Voor de frontend maken we gebruik van Angular en sass. In Grunt hebben we tasks die constant door de ontwikkelaars worden uitgevoerd of tijdens een build lopen. Mante gaat hier straks nog meer over vertellen.
Apps binnen Astad zijn ofwel in PHP(met ZF2) ofwel in NodeJs(Expressjs) geschreven. In het algemeen werden de meer complexere in PHP geschreven maar tegenwoordig schrijven we alle nieuwe apps in NodeJs
JB gaat her sevens nog wat meer over vertellen.
Als loadbalancer gebruiken e in onze webserver
Alle data van de apps zit in MongoDb. Redis gebruiken we om de sessies in te bewaren, caching te doen en pubsub voor onze websockets. Zoals Kris al aanhaalde zit alle data voor de zoek SolR.
Alles wat achter de bus zit werd vroeger bewaard in MS SQL en voor alle nieuwe apps in PostgreSQL.
De ESB die we gebruiken is van WSO2 en de huidige API manager is ook van WS02.
Recent is er een ACPAAS project gestart om een nieuwe API manager op te zetten met Kong.
Alle servers draaien op ubuntu en worden volledig gecheffed. Ondertussen werkt iedere developer ook met een vagrant box die exact dezelfde setup heeft als de productie omgeving.
De ontwikkelaars kunnen newrelic gebruiken om performantie problemen te achterhalen en de infrastructuur mensen gebruiken zabbix.
Wij zijn recent geswitched naar een volledige ALM stack van Atlassian.
Jira voor onze issues
Stash voor alle servers
Bamboo om de builds en deployments te triggeren, als er testen falen wordt er niets gebuild
Slack voor onze interne communicatie
automatiseer alles van de eerste dag.
maak gewoon 1 vagrant op basis van de cookbooks/config die in productie gebruikt wordt. In het begin werkte de meeste developers gewoon lokaal maar aangezien iedereen een andere setup heeft waren er altijd wel dingen die niet werkte. Daarna waren er meerdere vagrant files in omloop.
Als je verschillende gelijkaardige apps hebt kan je best 1 skeleton project maken en alle dependencies in packages onderbrengen. Op deze manier kan je later alles nog relatief gemakkelijk onderbrengen.
Voorzie zowel uw API als packages met een deftige semver versioning.
probeer het pull request systeem zo snel mogelijk op te zetten en durf ook zaken afkeuren
testen afdwingen en zorg ervoor dat een developer de testen niet kan afzetten
te vaak “we fixen da dan wel” wordt gezeAnders krijgt de ontwikkelaar deze indruk.
gebruik agile niet als een excuus om niet meer na te denken. Uiteindelijk komt het toch bij de developer terecht die tegen het einde van de sprint zijn code er in hackt. En uiteindelijk heb je een gefrustreerde developer die meer technical debt heeft opgeleverd.
100 zaken om mee rekening te houden, iets van af moet weten. Ik licht kort even toe.
Start development: vagrant
Vagrant = viruteel linux draaien
Ubuntu
Gedaan met het werkt op mijn systeem excuus
Nog maar 1 iets onthouden
we werken met stash voor versioning
da’s dus git
PR’s gitflow
Vagrant = viruteel linux draaien
CODE REVIEWS
CODE REVIEWS
IE 4000+ selectors -> bless
IE 4000+ selectors -> bless
IE 4000+ selectors -> bless
IE 4000+ selectors -> bless
IE 4000+ selectors -> bless
IE 4000+ selectors -> bless
IE 4000+ selectors -> bless
oorzaak: meestal door een bugfix
ook door toevoegen features
(Kan business en technische oorzaak hebben)
Voorbeeld: aanpassen van een component maar er niet aan denken dat deze ergens anders ook gebruikt wordt.