Beste allemaal,

Ik ben op zoek naar een oplossing om mijn PHP Web Applicatie (gebouwd met PHP, MySQL, jQuery, HTML, CSS) te hosten.
Het gaat om een Web Applicatie voor in de Horeca. Denk aan online inroosteren, inklokken, user management, bestellen etc.

Het uiteindelijke doel is om de web applicatie online te hosten zodat de restaurant vestigingen via het internet erbij kunnen.
Elk restaurant krijgt een eigen Web Applicatie en kan inmiddels door in te loggen gebruik maken van de web app.

Over de web app:
- Support laatste versie PHP
- Gebouwd op performance en veel javascript/jQuery om zoveel mogelijk dynamic content te genereren en dus zo weinig mogelijk scripts opnieuw te laden
- Maakt gebruik van lichte query's uit de MySQL database, sommige zijn zwaarder dan andere, denk aan DataTables vullen met data van de database. De zwaarste query is misschien wel alle producten uit de database halen wat per restaurant kan verschillen maar neem even voor het gemak 300 producten.
- Zowel medewerkers als hogere machten kunnen inloggen met verschillende rechten om verschillende taken te voltooien/in te zien

Uiteindelijke doel:
- Bijvoorbeeld 15 restaurants maken gebruik van de web app.
- Ieder restaurant eigen web app
- Moet gedurende de hele dag beschikbaar zijn op mobiel/tablet/laptop etc.

Mijn inzicht/oplossing:
_____________________________________________________________
1) Managed VPS waarbij je +- 10 klanten op 1 VPS laat draaien en elke restaurant een unieke subdomein geeft. Bijvoorbeeld: je hebt restaurant1 t/m restaurant10. Dan kan restaurant1 bij de web app dmv de volgende URL:
https://restaurant1.server01.nl/ en restaurant5 kan dan bij https://restaurant5.server01.nl/ (Met server01 uiteraard een andere domeinnaam)
- Link Managed VPS die ik gezien heb: https://www.sohosted.com/managed-vps/ (SMALL)
- TransIP VPS zien er ook goed uit, alleen weet ik niet of daar een managed VPS bij zit

Voordeel van Managed VPS:
- Hogere uptime (restaurant moet dag en nacht kunnen werken met het systeem zonder storingen)
- 24/7 storingdienst
- Sneller dan normale webhosting (weet ik niet zeker, zou een hele zware normale webhosting niet gewoon sneller zijn dan de goedkoopste managed VPS?)
- Goedkoper om te scalen

Nadelen:
- Op dit moment duur, omdat er nog geen betaalde klanten zijn
_____________________________________________________________

2) (Rond de 20 euro p/maand) Een normale zware webhosting. Waarbij je misschien maximaal 3 klanten per hosting laat draaien. En voor iedere 3 klanten een nieuwe hosting aanschaft.
- Link zware hosting die ik gezien heb: https://www.antagonist.nl/webhosting/ (Pro pakket)

Voordelen hosting:
- Op begin goedkoop, maar later duurder door scaling
- Qua snelheid misschien niet zo gek veel verschillend als goedkope managed VPS?
- Minder klanten per hosting pakket

Nadelen
- Later duurder door scaling
- Onoverzichtelijk, uiteindelijk heel veel hostingpakketten voor klanten
- Grotere kans op storingen
_____________________________________________________________

Op dit moment zijn dit de enige oplossingen die ik kan bedenken. Zelf geen technische kennis om bijvoorbeeld een unmanaged VPS op te zetten.

Als er iemand anders nog ideeën heeft om deze web app zo goed mogelijk te kunnen hosten voor de restaurants, graag! Ik sta open voor suggesties.
Transacties! Dan kan je fouten meteen terugdraaien.
Hmm, ik weet nog niet goed hoe dat in z'n praktijk gaat.
Hoe weet een transactie dat het moet rollbacken/niet committen als er een bepaalde conditie niet in de query staat.
SELECT * FROM table
of
SELECT * FROM table WHERE restaurantId=?
Zijn allebei goede query's en worden allebei uitgevoerd. Alleen willen we de eerste vermijden, omdat we daar de conditie zijn vergeten te checken.
Top! Bedankt voor alle info.
Hier kan ik zeker wat mee.
Op dit moment zijn we nog druk met het ontwikkelen van de web app zelf.
Zodra dit helemaal klaar moeten we het een en ander ombouwen om zo als 1 database en 1 codebase te functioneren. Maar scheelt het wel erg veel tijd in de toekomst.
Dit los je niet op met trucs en tests, maar met een goede softwarearchitectuur. Als je het geheel objectgeoriënteerd bouwt, kan één specifiek restaurant nooit worden verward met een lijst met meerdere restaurants: dat zijn twee compleet verschillende objecten.
Ward van der Put op 02/07/2019 14:01:38

Dit los je niet op met trucs en tests, maar met een goede softwarearchitectuur. Als je het geheel objectgeoriënteerd bouwt, kan één specifiek restaurant nooit worden verward met een lijst met meerdere restaurants: dat zijn twee compleet verschillende objecten.


Op dit moment is de web app vooral opgebouwd uit losstaande PHP codes en query's en zijn maar enkele dingen in objecten/classes opgeslagen.
Het klopt op dit moment vooral allemaal op de front-end. De back-end werkt, maar kan zeker beter.

Hoe zou een specifiek restaurant nooit verward kunnen worden met een lijst met meerdere restaurants door gebruik te maken van objectorientated programming volgens jou?

Ik zou wat praktische voorbeelden erg waarderen.
Stel je hebt 1 database voor 100 restaurants. En alle restaurants bij elkaar hebben 30.000 producten. Dan lijkt het mij langer te duren om SELECT * FROM producten WHERE restaurant=1 uit te voeren op die database met 30.000 rijen dan voor elk restaurant een aparte database te hebben.

Heb je dit al eens getoetst? Oftewel getest op de hardware waar deze database(s) actief zou(den) moeten zijn? Als je indexen aanmaakt dan zou dit niet zoveel uit moeten maken. Zoals iemand ooit eens zei: meten = weten.

En dan is het bovenstaande slechts één argument: performance. Maar is er uit optiek van de data zelf een reden om alles in één database te stoppen? En misschien zou je het app-beheer (site voor beheren restaurants) en het app-gebruik (site/subdomein voor restaurant zelf) uit elkaar kunnen trekken?

Mogelijk moet je ook gaan nadenken hoe je het toevoegen van een restaurant automatisch kunt uitrollen (toevoegen nieuw restaurant in beheer database, creëren nieuwe bijbehorende restaurant-database, als je deze aanpak kiest). Hier valt een heleboel te automatiseren lijkt mij.

Dus stel je moet ooit eens een database tabel aanpassen of toevoegen, moet je dat ook per restaurant weer gaan updaten.

En dat is een indicatie dat je ook moet nadenken over upgrades en eventueel maatwerk die je bijvoorbeeld in modules en restaurant-specifieke tabellen stopt, zodat je je core-functionaliteit (van zowel code als database) gelijk houdt zodat je met je updates geen maatwerk overschrijft :).

Hoe meer aandacht je besteedt aan het ontwerp, hoe minder ellende down the line, lijkt mij. En ja, dit zouden alle echte relationele databases moeten zijn, zodat onderlinge data consistent blijft.

@Ariën, transacties zijn onderling niet (per definitie) atomair. Gaat dat voorbeeld wel goed in die tutorial? Wat nu als twee van dat soort transacties tegelijkertijd uitgevoerd worden? Ik vermoed dat dat alsnog vreselijk mis kan gaan met het orderNumber? Of de laatst gecommitte faalt gewoon omdat het orderNumber dan al is ingenomen? En waarom gebruikt die gast niet gewoon een auto-increment veld? :p
Stan Avoird op 02/07/2019 13:16:11

SELECT * FROM table
of
SELECT * FROM table WHERE restaurantId=?


Die tweede query programmeer je maar één keer: in een object (een mapper bijvoorbeeld) dat een ander object (een restaurant) uit de database haalt. De rest van de code laat je uitsluitend met dat tweede object als model werken.

Je programmeert sowieso alles maar één keer. DRY: Don’t Repeat Yourself. Er is veel voor te zeggen om ook de eerste query maar op één plek in je gehele architectuur te laten voorkomen.

Dit maakt je oplossing robuust en schaalbaar: de component die verantwoordelijk is voor "haal alle restaurants op" kun je verbouwen of vervangen zonder dat het gevolgen voor de rest heeft.
Heb je dit al eens getoetst? Oftewel getest op de hardware waar deze database(s) actief zou(den) moeten zijn? Als je indexen aanmaakt dan zou dit niet zoveel uit moeten maken. Zoals iemand ooit eens zei: meten = weten.

En dan is het bovenstaande slechts één argument: performance. Maar is er uit optiek van de data zelf een reden om alles in één database te stoppen? En misschien zou je het app-beheer (site voor beheren restaurants) en het app-gebruik (site/subdomein voor restaurant zelf) uit elkaar kunnen trekken?

Mogelijk moet je ook gaan nadenken hoe je het toevoegen van een restaurant automatisch kunt uitrollen (toevoegen nieuw restaurant in beheer database, creëren nieuwe bijbehorende restaurant-database, als je deze aanpak kiest). Hier valt een heleboel te automatiseren lijkt mij.


Nog niet getoetst. Maar inderdaad, als je gewoon een index aanmaakt die dat sorteert. Heeft het niks te maken met de andere rijen die in de database tabel staan.

Het zou voor de scalability beter zijn als je alle data van alle restaurants in 1 database propt.
Hiermee wordt 1 database dan wel erg groot en moet je specifieke query's schrijven die sorteren op index restaurantId, maar de backups gaan simpeler, want je hoeft maar 1 database te backupen. Ook het updaten van de database structuur hoeft maar 1x uitgevoerd te worden wanneer nodig is, omdat er maar 1 database is.

Mij lijkt dit dus wel een goede oplossing. Echter heb ik nogsteeds niet echt een overzicht gekregen voor mezelf hoe ik al de huidige query's wil gaan omschrijven dat ze die specifieke "WHERE restaurantId=?" conditie gaan gebruiken.
In 80% van mijn web app gebruik ik wel een functie die gemakkelijk een query prepared en dan executed.
Als ik deze 80% , 100% kan maken. Dan kan ik vóórdat deze functie de query executed, checken of de query de specifieke conditie "WHERE restaurantId=?" bevat.
Zo niet, dan wordt de query gewoon niet uitgevoerd.
Zo vermijd ik dat er per ongeluk query's worden uitgevoerd voor alle restaurants in plaats van een specifieke restaurant.

Het toevoegen van een restaurant zou in principe vrij simpel moeten zijn als ik de volgende structuur aanhoudt:
Dus het eindscenario zou kunnen zijn:
- 1 PHP codebase in de public_html folder
- 1 PHP config file voor database in een root folder, niet direct accessable
- 1 Subdomein per restaurant
- Bij elk PHP request/pageload check subdomein en aan de hand daarvan set een database OF set een restaurantId waarmee je uit de database kan lezen
- 1 Database voor alle restaurants. Gegevens gescheiden dmv unieke index restaurantId bijv.
- Alles draaiend op een managed VPS


Nieuw restaurant/klant:
1. Maak nieuwe subdomein aan
2. Update een bestandje of database dat een uniek restaurantId aan het nieuwe subdomein koppelt
3. Klaar?

Wanneer het nieuwe restaurant naar haar subdomein gaat, wordt bij aanvang op de web app gevraagd welk restaurantId er bij het subdomein wordt en wordt deze gebruikt voor alle verdere acties op de web app.

Waar ik trouwens nog niet over heb nagedacht is bijvoorbeeld het uploaden van een logo of bestand. Nu wordt dit geupload naar een specifieke FTP map bijvoorbeeld: server01.nl/uploads/logo.png
Hier moet ik ook iets op gaan verzinnen dat de bestanden gescheiden blijven per restaurant. En dat een restaurant niet zomaar bij de files kan van een ander restaurant.

Die tweede query programmeer je maar één keer: in een object (een mapper bijvoorbeeld) dat een ander object (een restaurant) uit de database haalt. De rest van de code laat je uitsluitend met dat tweede object als model werken.

Je programmeert sowieso alles maar één keer. DRY: Don’t Repeat Yourself. Er is veel voor te zeggen om ook de eerste query maar op één plek in je gehele architectuur te laten voorkomen.

Dit maakt je oplossing robuust en schaalbaar: de component die verantwoordelijk is voor "haal alle restaurants op" kun je verbouwen of vervangen zonder dat het gevolgen voor de rest heeft.


Ik begin meer in te zien hoe dit werkt in de praktijk.
Moet ik dan functies gaan maken die een query generaliseren?
Bijvoorbeeld ik wil een rij updaten in table "producten" waar de naam van het product "Appel" is.
Nu is er gewoon een losse query:
$conn->query("UPDATE producten SET amount=1 WHERE name='Appel'");
Maar we willen, als we alles in 1 database stoppen, naar de volgende query:
$conn->query("UPDATE producten SET amount=1 WHERE name='Appel' AND restaurantId=1");

Hoe kan ik dit het beste aanpakken dan?
Door een functie en class bijvoorbeeld aan te maken die iets generieks uitvoert?
Restaurant->updateQuery($table, $SET, $WHERE);
waarbij updateQuery er ongeveer zo uitziet:
function updateQuery($table, $SET, $WHERE) {
//functie staat in class Restaurant, en class Restaurant bevat al restaurantId=1
$conn->query("UPDATE $table SET $SET WHERE $WHERE AND restaurantId=".restaurantId);
}

Dit is een zeer simpel voorbeeld, maar denk ik in de goede richting?

Bedankt iedereen nogmaals voor het meedenken!
Implementatie is echt de laatste stap. Immers, als je een goed plan hebt, dan is de implementatie enkel de "smaak" waarin je het plan ten uitvoer brengt. Deze kun je te allen tijde aanpassen, bijschaven en verbeteren zolang deze voldoet aan de specificatie. Ik zou dus op dit moment (nog) niet focussen op concrete implementaties. Wel zou je "uitstapjes" kunnen maken om te zien of hoe een bepaalde aanpak zou moeten lopen (proof of concepts). Maar dingen uitwerken op grond van code... I don't know man :).

Reageren