SlideShare a Scribd company logo
1 of 83
Download to read offline
How to build customizable
multitenant web applications
       Stephan Hochdörfer, bitExpert AG




  "Building an application so customizable it's the last
          application you'll ever need to build"
                     Harrie Verveer
About me
 Stephan Hochdörfer, bitExpert AG

 Department Manager Research Labs

 enjoying PHP since 1999

 S.Hochdoerfer@bitExpert.de

 @shochdoerfer
Single Tenancy
Developer   vs.   Businessman
Single Tenancy – more customers
Single Tenancy – even more customers
Where will this lead to?
Maintenance
nightmare!
Single Tenancy

                  Tenant 1



                 Application



                 Database



                 Hardware
Single Tenancy

        Tenant 1      Tenant 2      Tenant 3



       Application   Application   Application



       Database      Database      Database



       Hardware      Hardware      Hardware
Multi Tenancy

       Tenant 1    Tenant 2     Tenant 3



                  Application



                  Database



                  Hardware
What should be customizable?
What should be customizable?

       Tenant 1    Tenant 2     Tenant 3



                  Application



                  Database



                  Hardware
What should be customizable?

       Tenant 1    Tenant 2     Tenant 3



                  Application



                  Database



                  Hardware
Frontend | Branding


 How to skin an application?
Application | Frontend


 How to skin an application?



                         Remember:
                   It`s a web application!
Application | Frontend


 How to skin an application?




                         HTML
Application | Frontend


 How to skin an application?




                         HTML + CSS
Application | Frontend
Application | Frontend
Application | Frontend
Application | Frontend


 How to customize?
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>My App</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link rel="stylesheet" type="text/css" href="css/styles/myapp.css" />
  </head>
  <body>
  </body>
  </html>
Application | Frontend


 How to customize?
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>My App</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link rel="stylesheet" type="text/css" href="css/styles/<?php echo
  $tenant ?>.css" />
  </head>
  <body>
  </body>
  </html>
Application | Frontend


 How to customize?
  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
  <html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <title>My App</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <link rel="stylesheet" type="text/css" href="css/styles/myapp.css" />
    <link rel="stylesheet" type="text/css" href="css/styles/<?php echo $tenant ?
  >.css" />
  </head>
  <body>
  </body>
  </html>
Application | Frontend


 Feature driven CSS




                         Wait, there`s more...
Application | Frontend


 Feature driven CSS




                         display: none
Application | Frontend


 Next level...
Application | Backend


 Menubar generation
 <?php

 if($user->hasEnabled(Module::ORDERMANAGEMENT))
 {
   if($user->canAccess(OrderManagement::LIST_ORDERS))
   {
     $this->renderLink(OrderManagement::LIST_ORDERS);
   }

     if($user->canAccess(OrderManagement::ADD_ORDER))
     {
       $this->renderLink(OrderManagement::ADD_ORDER);
     }

     if($user->canAccess(OrderManagement::CANCEL_ORDER))
     {
       $this->renderLink(OrderManagement::CANCEL_ORDER);
     }
 }
Application | Backend


 Menubar generation
 <?php

 if($tenant->hasModule(Module::ORDERMANAGEMENT)
 {
   if($user->hasEnabled(Module::ORDERMANAGEMENT))
   {
     if($user->canAccess(OrderManagement::LIST_ORDERS))
     {
       $this->renderLink(OrderManagement::LIST_ORDERS);
     }

         if($user->canAccess(OrderManagement::ADD_ORDER))
         {
           $this->renderLink(OrderManagement::ADD_ORDER);
         }
     }
 }
Application | Backend


 Menubar generation




                        Modularize!
Application | Backend


 Menubar generation



                 Module 1      Module 2        Module 3

                                                    register at start up

                            Application core
Application | Backend


 Menubar generation

              Configuration for tenant 1


                 Module 1            Module 2       Module 3

                                                         register at start up

                                 Application core
Application | Backend


 Menubar generation

              Configuration for tenant 2


                 Module 1            Module 2       Module 3

                                                         register at start up

                                 Application core
Application | Backend


 Optimize workflows
 <?php

 if('CC' == $paymentType)
 {
   // handle credit card payment
 }
 else if('COD' == $paymentType)
 {
   // handle cash on delivery payment
 }
Application | Backend


 Optimize workflows
 <?php

 if('CC' == $paymentType)
 {
   // handle credit card payment for some tenants!
   if(in_array($tenant->getName(), array('tenant1', 'tenant2'))
   {
     // insert logic here...
   }
 }
 else if('COD' == $paymentType)
 {
   // handle cash on delivery payment for some tenants!
   if(in_array($tenant->getName(), array('tenant3'))
   {
     // insert logic here...
   }
 }
Application | Backend


 Optimize workflows




                  Decouple functionality!
Application | Backend


 Optimize workflows
 <?php

 $paymentType = 'CC'; // set via request
 $payment     = PaymentFactory::create($paymentType);

 $payment->execute($order);
Application | Backend


 Optimize workflows
 <?php

 $paymentType = 'CC'; // set via request
 $payment     = PaymentFactory::create($paymentType, $tenant);

 $payment->execute($order);
Application | Backend


 Optimize workflows




               How to add custom logic?
Application | Backend


 Custom logic - Subclassing?

                               Abstract
                               Payment


                                 CC
                               Payment


                        CCPayment     CCPayment
                         Tenant 1      Tenant 2
Application | Backend


 Custom logic




                        Any alternatives?
Application | Backend


 Custom logic




                        Let`s add hooks...
Application | Backend


 Custom logic - Hooks
 <?php

 $paymentType = 'CC'; // set via request
 $payment     = PaymentFactory::create($paymentType, $tenant);

 if($this->paymentPreProcessor instanceof IPaymentPreProcessor) {
   $this->paymentPreProcessor->run($payment, $tenant, $order);
 }

 $payment->execute($order);

 if($this->paymentPostProcessor instanceof IPaymentPostProcessor) {
   $this->paymentPostProcessor->run($payment, $tenant, $order);
 }
Application | Backend


 Custom logic




         How to set the dependencies?
Application | Backend


 Custom logic




                Inject the dependencies!
Application | Backend


 Custom logic – Dependency Injection
 <?xml version="1.0" encoding="UTF-8" ?>
 <beans xmlns="http://www.bitexpert.de/schema"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.bitexpert.de/schema/
        http://www.bitexpert.de/schema/bitFramework-beans.xsd">

      <bean id="MyApp.Service.Order" class="MyAppServiceOrder.php">
      </bean>

      <bean id="Tenant1.Service.Order" class="MyAppServiceOrder.php">
          <property name="paymentPreProcessor"
                      ref="Tentant1.Payment.PaymentValidation" />
      </bean>

     <bean id="Tenant2.Service.Order" class="MyAppServiceOrder.php">
          <property name="paymentPreProcessor"
                     ref="Tentant2.Payment.StrictValidation" />
          <property name="paymentPostProcessor"
                     ref="Tentant2.Payment.PushOrderToSAP" />
     </bean>
 </beans>
Application | Backend


 Custom logic




                        Any improvements?
Application | Backend


 Custom logic
 <?php

 $paymentType = 'CC'; // set via request
 $payment     = PaymentFactory::create($paymentType, $tenant);

 if($this->paymentPreProcessor instanceof IPaymentPreProcessor) {
   $this->paymentPreProcessor->run($payment, $tenant, $order);
 }

 $payment->execute($order);

 if($this->paymentPostProcessor instanceof IPaymentPostProcessor) {
   $this->paymentPostProcessor->run($payment, $tenant, $order);
 }
Application | Backend


 Custom logic
 <?php

 $paymentType = 'CC'; // set via request
 $payment     = PaymentFactory::create($paymentType, $tenant);

 if($this->paymentPreProcessor instanceof IPaymentPreProcessor) {
   $this->paymentPreProcessor->run($payment, $tenant, $order);
 }

 $payment->execute($order);

 if($this->paymentPostProcessor instanceof IPaymentPostProcessor) {
   $this->paymentPostProcessor->run($payment, $tenant, $order);
 }
Application | Backend


 Custom logic




         Aspect-oriented programming
Application | Backend


 Custom logic – Aspects for the masses!
 /**
  * @aspect
  */
 class CustomPaymentProcessingAspect {
     /**
      * @around MyAppServiceOrder->processPayment
      */
     public function customFilter(AOPJoinPointInterface $joinPoint) {
          // @TODO: implement pre-processing logic
          // ...

           $result = $joinPoint->getAdviceChain()->proceed($joinPoint);

           // @TODO: implement post-processing logic
           // ...

           return $result;
      }
 }
Application | Backend


 Custom logic - Result
 <?php

 $paymentType = 'CC'; // set via request
 $payment     = PaymentFactory::create($paymentType, $tenant);

 $payment->execute($order);
Application | Backend


 Next level...
Database


 Database – Where to store the data?
Database


 Database – Where to store the data?




           We need to store data for a tenant!
Database


 Database – Where to store the data?


           Database per Tenant?
Database


 Database – Where to store the data?


           Database per Tenant?

            Schema per Tenant?
Database


 Database – Where to store the data?


           Database per Tenant?

            Schema per Tenant?

             Tenant Id per Row?
Database


 Database – How to access the data?




                    vs.




           ORM             dynamic statements
What`s beyond?




                 Generalize you should!
What`s beyond?


 Multi Tenancy




                 Software system family
What`s beyond?


 No single solution!
What`s beyond?


 A factory for mass production!
What`s beyond?


 Multi Tenancy – Single Instance

                 Tenant 1    Tenant 2     Tenant 3



                            Application



                            Database



                            Hardware
What`s beyond?


 Multi Tenancy – Multi Instance

                 Tenant 1    Tenant 2     Tenant 3



                            Application



                            Database



                            Hardware
What`s beyond?


 Generative Programming

                         Configuration
                          Configuration




                                                         1 ... n
           Implementation
            Implementation                  Generator     Product
             components                      Generator     Product
              components




                             Generator
                              Generator
                             application
                              application
What`s beyond?


 Generative Programming

                         Configuration
                          Configuration

                                                         Tenant 1
                                                          Tenant 1


           Implementation
            Implementation                  Generator
             components                      Generator
              components



                                                         Tenant x
                                                          Tenant x
                             Generator
                              Generator
                             application
                              application
What`s beyond?


 Generative Programming - Goal




     Create an optimized application!
What`s beyond?


 Generative Programming - Goal




     Create an optimized application
             for one tenant!
What`s beyond?


 Generative Programming – Bonus points




       Reduce application complexity
What`s beyond?


 Generative Programming – Bonus points
  FileFrm FILEOrderService_php5 {
    private String PreProcessor = "";
    private String PostProcessor = "";

    public FILEOrderService_php5() {
      setFilename("Order.php5");
      setRelativePath("/classes/MyApp/Service");
    }

    private void assign() {
  BEGINCONTENT()
  <?php

  $paymentType = 'CC'; // set via request
  $payment     = PaymentFactory::create($paymentType, $tenant);

  <!{PreProcessor}!>
  $payment->execute($order);
  <!{PostProcessor}!>
  ENDCONTENT()
    }
  }
What`s beyond?


 Generative Programming – Bonus points
  FileFrm FILEOrderService_php5 {
    [...]

      private void configure() {
        if(this.getConfiguration().hasFeature('PreProcessor')) {
          PreProcessor = this.getPreProcessorContent(
             this.getConfiguration.getTenant()
          );
        }

          if(this.getConfiguration().hasFeature('PostProcessor')) {
            PostProcessor = this.getPostProcessorContent(
               this.getConfiguration.getTenant()
            );
          }
      }
  }
Application | Backend


 Generative Programming – Bonus points
 Example:
 Preprocessor:
 Postprocessor:



 Output:
Application | Backend


 Generative Programming – Bonus points
 Example:
 Preprocessor:
 Postprocessor:



 Output:
 <?php

 $paymentType = 'CC'; // set via request
 $payment     = PaymentFactory::create($paymentType);

 $payment->execute($order);
Application | Backend


 Generative Programming – Bonus points
 Example:
 Preprocessor: $this->paymentPreProcessor->run($payment, $tenant, $order);
 Postprocessor:



 Output:
Application | Backend


 Generative Programming – Bonus points
 Example:
 Preprocessor: $this->paymentPreProcessor->run($payment, $tenant, $order);
 Postprocessor:



 Output:
 <?php

 $paymentType = 'CC'; // set via request
 $payment     = PaymentFactory::create($paymentType);

 $this->paymentPreProcessor->run($payment, $tenant, $order);

 $payment->execute($order);
What`s beyond?


 Generative Programming – Bonus points




        Reduce maintenance support
What`s beyond?


 Generative Programming – Bonus points




                          Implementation
             Feature
                            component
What`s beyond?


 Generative Programming – Bonus points




                    Feature




                 Implementation
                   component
What`s beyond?


 Generative Programming – Bonus points

                   Customer




                    Feature




                 Implementation
                   component
What`s beyond?


 Generative Programming – The book
http://joind.in/2497
Flickr Credits
http://www.flickr.com/photos/andresrueda/3452940751/
http://www.flickr.com/photos/andresrueda/3455410635/

More Related Content

What's hot

AngularJS with Slim PHP Micro Framework
AngularJS with Slim PHP Micro FrameworkAngularJS with Slim PHP Micro Framework
AngularJS with Slim PHP Micro FrameworkBackand Cohen
 
AK 3 web services using apache axis
AK 3   web services using apache axisAK 3   web services using apache axis
AK 3 web services using apache axisgauravashq
 
Workshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJSWorkshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJSVisual Engineering
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasminePaulo Ragonha
 
Java Web Programming [8/9] : JSF and AJAX
Java Web Programming [8/9] : JSF and AJAXJava Web Programming [8/9] : JSF and AJAX
Java Web Programming [8/9] : JSF and AJAXIMC Institute
 
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...Atlassian
 
Dart for Java Developers
Dart for Java DevelopersDart for Java Developers
Dart for Java DevelopersYakov Fain
 
No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...
No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...
No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...Atlassian
 
IndexedDB - Querying and Performance
IndexedDB - Querying and PerformanceIndexedDB - Querying and Performance
IndexedDB - Querying and PerformanceParashuram N
 
Building scalable applications with angular js
Building scalable applications with angular jsBuilding scalable applications with angular js
Building scalable applications with angular jsAndrew Alpert
 
Building Single Page Application (SPA) with Symfony2 and AngularJS
Building Single Page Application (SPA) with Symfony2 and AngularJSBuilding Single Page Application (SPA) with Symfony2 and AngularJS
Building Single Page Application (SPA) with Symfony2 and AngularJSAntonio Peric-Mazar
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverSpike Brehm
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular IntermediateLinkMe Srl
 
Dependency Injection in PHP - dwx13
Dependency Injection in PHP - dwx13Dependency Injection in PHP - dwx13
Dependency Injection in PHP - dwx13Stephan Hochdörfer
 
Real World Dependency Injection - phpugffm13
Real World Dependency Injection - phpugffm13Real World Dependency Injection - phpugffm13
Real World Dependency Injection - phpugffm13Stephan Hochdörfer
 
Workshop 2: JavaScript Design Patterns
Workshop 2: JavaScript Design PatternsWorkshop 2: JavaScript Design Patterns
Workshop 2: JavaScript Design PatternsVisual Engineering
 
Speed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSocketsSpeed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSocketsYakov Fain
 
Refactoring Large Web Applications with Backbone.js
Refactoring Large Web Applications with Backbone.jsRefactoring Large Web Applications with Backbone.js
Refactoring Large Web Applications with Backbone.jsStacy London
 
Things Your Mother Didnt Tell You About Bundle Configurations - Symfony Live…
Things Your Mother Didnt Tell You About Bundle Configurations - Symfony Live…Things Your Mother Didnt Tell You About Bundle Configurations - Symfony Live…
Things Your Mother Didnt Tell You About Bundle Configurations - Symfony Live…D
 

What's hot (20)

AngularJS with Slim PHP Micro Framework
AngularJS with Slim PHP Micro FrameworkAngularJS with Slim PHP Micro Framework
AngularJS with Slim PHP Micro Framework
 
AK 3 web services using apache axis
AK 3   web services using apache axisAK 3   web services using apache axis
AK 3 web services using apache axis
 
Workshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJSWorkshop 27: Isomorphic web apps with ReactJS
Workshop 27: Isomorphic web apps with ReactJS
 
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and JasmineSingle Page Web Applications with CoffeeScript, Backbone and Jasmine
Single Page Web Applications with CoffeeScript, Backbone and Jasmine
 
Java Web Programming [8/9] : JSF and AJAX
Java Web Programming [8/9] : JSF and AJAXJava Web Programming [8/9] : JSF and AJAX
Java Web Programming [8/9] : JSF and AJAX
 
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
No Coding Necessary: Building Confluence User Macros Cheat Sheet - Atlassian ...
 
Dart for Java Developers
Dart for Java DevelopersDart for Java Developers
Dart for Java Developers
 
No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...
No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...
No Coding Necessary: Building User Macros and Dynamic Reports Inside Confluen...
 
IndexedDB - Querying and Performance
IndexedDB - Querying and PerformanceIndexedDB - Querying and Performance
IndexedDB - Querying and Performance
 
Building scalable applications with angular js
Building scalable applications with angular jsBuilding scalable applications with angular js
Building scalable applications with angular js
 
Building Single Page Application (SPA) with Symfony2 and AngularJS
Building Single Page Application (SPA) with Symfony2 and AngularJSBuilding Single Page Application (SPA) with Symfony2 and AngularJS
Building Single Page Application (SPA) with Symfony2 and AngularJS
 
Introducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and serverIntroducing Rendr: Run your Backbone.js apps on the client and server
Introducing Rendr: Run your Backbone.js apps on the client and server
 
Angular Intermediate
Angular IntermediateAngular Intermediate
Angular Intermediate
 
Dependency Injection in PHP - dwx13
Dependency Injection in PHP - dwx13Dependency Injection in PHP - dwx13
Dependency Injection in PHP - dwx13
 
Real World Dependency Injection - phpugffm13
Real World Dependency Injection - phpugffm13Real World Dependency Injection - phpugffm13
Real World Dependency Injection - phpugffm13
 
Workshop 2: JavaScript Design Patterns
Workshop 2: JavaScript Design PatternsWorkshop 2: JavaScript Design Patterns
Workshop 2: JavaScript Design Patterns
 
Basics of AngularJS
Basics of AngularJSBasics of AngularJS
Basics of AngularJS
 
Speed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSocketsSpeed up your Web applications with HTML5 WebSockets
Speed up your Web applications with HTML5 WebSockets
 
Refactoring Large Web Applications with Backbone.js
Refactoring Large Web Applications with Backbone.jsRefactoring Large Web Applications with Backbone.js
Refactoring Large Web Applications with Backbone.js
 
Things Your Mother Didnt Tell You About Bundle Configurations - Symfony Live…
Things Your Mother Didnt Tell You About Bundle Configurations - Symfony Live…Things Your Mother Didnt Tell You About Bundle Configurations - Symfony Live…
Things Your Mother Didnt Tell You About Bundle Configurations - Symfony Live…
 

Similar to How to build customizable multitenant web applications - PHPBNL11

How to build customizable multitenant web applications - IPC11 Spring Edition
How to build customizable multitenant web applications - IPC11 Spring EditionHow to build customizable multitenant web applications - IPC11 Spring Edition
How to build customizable multitenant web applications - IPC11 Spring EditionStephan Hochdörfer
 
Benefit of CodeIgniter php framework
Benefit of CodeIgniter php frameworkBenefit of CodeIgniter php framework
Benefit of CodeIgniter php frameworkBo-Yi Wu
 
Reactive Application Using METEOR
Reactive Application Using METEORReactive Application Using METEOR
Reactive Application Using METEORNodeXperts
 
Reactive application using meteor
Reactive application using meteorReactive application using meteor
Reactive application using meteorSapna Upreti
 
Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi
Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry PiGrâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi
Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry PiJérémy Derussé
 
Only JavaScript, only Meteor.js
Only JavaScript, only Meteor.jsOnly JavaScript, only Meteor.js
Only JavaScript, only Meteor.jsTomáš Hromník
 
Open stack gbp final sn-4-slideshare
Open stack gbp final sn-4-slideshareOpen stack gbp final sn-4-slideshare
Open stack gbp final sn-4-slideshareSumit Naiksatam
 
Deploying configurable frontend web application containers
Deploying configurable frontend web application containersDeploying configurable frontend web application containers
Deploying configurable frontend web application containersJosé Moreira
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the TrenchesJonathan Wage
 
Building Multi-Tenant and SaaS products in PHP - CloudConf 2015
Building Multi-Tenant and SaaS products in PHP - CloudConf 2015Building Multi-Tenant and SaaS products in PHP - CloudConf 2015
Building Multi-Tenant and SaaS products in PHP - CloudConf 2015Innomatic Platform
 
"Asynchronous" Integration Tests for Microservices - RootConf 2017
"Asynchronous" Integration Tests for Microservices - RootConf 2017"Asynchronous" Integration Tests for Microservices - RootConf 2017
"Asynchronous" Integration Tests for Microservices - RootConf 2017Ramya Authappan
 
using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'sAntônio Roberto Silva
 
Stephane Lapointe, Frank Boucher & Alexandre Brisebois: Les micro-services et...
Stephane Lapointe, Frank Boucher & Alexandre Brisebois: Les micro-services et...Stephane Lapointe, Frank Boucher & Alexandre Brisebois: Les micro-services et...
Stephane Lapointe, Frank Boucher & Alexandre Brisebois: Les micro-services et...MSDEVMTL
 
Vue micro frontend implementation patterns
Vue micro frontend implementation patternsVue micro frontend implementation patterns
Vue micro frontend implementation patternsAlbert Brand
 
Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2 Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2 3camp
 
MongoDB.local Atlanta: Introduction to Serverless MongoDB
MongoDB.local Atlanta: Introduction to Serverless MongoDBMongoDB.local Atlanta: Introduction to Serverless MongoDB
MongoDB.local Atlanta: Introduction to Serverless MongoDBMongoDB
 
CQRS and Event Sourcing
CQRS and Event Sourcing CQRS and Event Sourcing
CQRS and Event Sourcing Inho Kang
 
[Hands-on] CQRS(Command Query Responsibility Segregation) 와 Event Sourcing 패턴 실습
[Hands-on] CQRS(Command Query Responsibility Segregation) 와 Event Sourcing 패턴 실습[Hands-on] CQRS(Command Query Responsibility Segregation) 와 Event Sourcing 패턴 실습
[Hands-on] CQRS(Command Query Responsibility Segregation) 와 Event Sourcing 패턴 실습Oracle Korea
 
Lean Php Presentation
Lean Php PresentationLean Php Presentation
Lean Php PresentationAlan Pinstein
 
Story about module management with angular.js
Story about module management with angular.jsStory about module management with angular.js
Story about module management with angular.jsDavid Amend
 

Similar to How to build customizable multitenant web applications - PHPBNL11 (20)

How to build customizable multitenant web applications - IPC11 Spring Edition
How to build customizable multitenant web applications - IPC11 Spring EditionHow to build customizable multitenant web applications - IPC11 Spring Edition
How to build customizable multitenant web applications - IPC11 Spring Edition
 
Benefit of CodeIgniter php framework
Benefit of CodeIgniter php frameworkBenefit of CodeIgniter php framework
Benefit of CodeIgniter php framework
 
Reactive Application Using METEOR
Reactive Application Using METEORReactive Application Using METEOR
Reactive Application Using METEOR
 
Reactive application using meteor
Reactive application using meteorReactive application using meteor
Reactive application using meteor
 
Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi
Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry PiGrâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi
Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi
 
Only JavaScript, only Meteor.js
Only JavaScript, only Meteor.jsOnly JavaScript, only Meteor.js
Only JavaScript, only Meteor.js
 
Open stack gbp final sn-4-slideshare
Open stack gbp final sn-4-slideshareOpen stack gbp final sn-4-slideshare
Open stack gbp final sn-4-slideshare
 
Deploying configurable frontend web application containers
Deploying configurable frontend web application containersDeploying configurable frontend web application containers
Deploying configurable frontend web application containers
 
Symfony2 from the Trenches
Symfony2 from the TrenchesSymfony2 from the Trenches
Symfony2 from the Trenches
 
Building Multi-Tenant and SaaS products in PHP - CloudConf 2015
Building Multi-Tenant and SaaS products in PHP - CloudConf 2015Building Multi-Tenant and SaaS products in PHP - CloudConf 2015
Building Multi-Tenant and SaaS products in PHP - CloudConf 2015
 
"Asynchronous" Integration Tests for Microservices - RootConf 2017
"Asynchronous" Integration Tests for Microservices - RootConf 2017"Asynchronous" Integration Tests for Microservices - RootConf 2017
"Asynchronous" Integration Tests for Microservices - RootConf 2017
 
using Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API'susing Mithril.js + postgREST to build and consume API's
using Mithril.js + postgREST to build and consume API's
 
Stephane Lapointe, Frank Boucher & Alexandre Brisebois: Les micro-services et...
Stephane Lapointe, Frank Boucher & Alexandre Brisebois: Les micro-services et...Stephane Lapointe, Frank Boucher & Alexandre Brisebois: Les micro-services et...
Stephane Lapointe, Frank Boucher & Alexandre Brisebois: Les micro-services et...
 
Vue micro frontend implementation patterns
Vue micro frontend implementation patternsVue micro frontend implementation patterns
Vue micro frontend implementation patterns
 
Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2 Osiąganie mądrej architektury z Symfony2
Osiąganie mądrej architektury z Symfony2
 
MongoDB.local Atlanta: Introduction to Serverless MongoDB
MongoDB.local Atlanta: Introduction to Serverless MongoDBMongoDB.local Atlanta: Introduction to Serverless MongoDB
MongoDB.local Atlanta: Introduction to Serverless MongoDB
 
CQRS and Event Sourcing
CQRS and Event Sourcing CQRS and Event Sourcing
CQRS and Event Sourcing
 
[Hands-on] CQRS(Command Query Responsibility Segregation) 와 Event Sourcing 패턴 실습
[Hands-on] CQRS(Command Query Responsibility Segregation) 와 Event Sourcing 패턴 실습[Hands-on] CQRS(Command Query Responsibility Segregation) 와 Event Sourcing 패턴 실습
[Hands-on] CQRS(Command Query Responsibility Segregation) 와 Event Sourcing 패턴 실습
 
Lean Php Presentation
Lean Php PresentationLean Php Presentation
Lean Php Presentation
 
Story about module management with angular.js
Story about module management with angular.jsStory about module management with angular.js
Story about module management with angular.js
 

More from Stephan Hochdörfer

Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...
Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...
Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...Stephan Hochdörfer
 
Phing for power users - frOSCon8
Phing for power users - frOSCon8Phing for power users - frOSCon8
Phing for power users - frOSCon8Stephan Hochdörfer
 
Offline strategies for HTML5 web applications - frOSCon8
Offline strategies for HTML5 web applications - frOSCon8Offline strategies for HTML5 web applications - frOSCon8
Offline strategies for HTML5 web applications - frOSCon8Stephan Hochdörfer
 
Offline Strategies for HTML5 Web Applications - oscon13
Offline Strategies for HTML5 Web Applications - oscon13Offline Strategies for HTML5 Web Applications - oscon13
Offline Strategies for HTML5 Web Applications - oscon13Stephan Hochdörfer
 
Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13Stephan Hochdörfer
 
Offline Strategien für HTML5 Web Applikationen - dwx13
Offline Strategien für HTML5 Web Applikationen - dwx13 Offline Strategien für HTML5 Web Applikationen - dwx13
Offline Strategien für HTML5 Web Applikationen - dwx13 Stephan Hochdörfer
 
Your Business. Your Language. Your Code - dpc13
Your Business. Your Language. Your Code - dpc13Your Business. Your Language. Your Code - dpc13
Your Business. Your Language. Your Code - dpc13Stephan Hochdörfer
 
Phing for power users - dpc_uncon13
Phing for power users - dpc_uncon13Phing for power users - dpc_uncon13
Phing for power users - dpc_uncon13Stephan Hochdörfer
 
Offline Strategies for HTML5 Web Applications - ipc13
Offline Strategies for HTML5 Web Applications - ipc13Offline Strategies for HTML5 Web Applications - ipc13
Offline Strategies for HTML5 Web Applications - ipc13Stephan Hochdörfer
 
Offline-Strategien für HTML5 Web Applikationen - wmka
Offline-Strategien für HTML5 Web Applikationen - wmkaOffline-Strategien für HTML5 Web Applikationen - wmka
Offline-Strategien für HTML5 Web Applikationen - wmkaStephan Hochdörfer
 
Offline-Strategien für HTML5 Web Applikationen - bedcon13
Offline-Strategien für HTML5 Web Applikationen - bedcon13Offline-Strategien für HTML5 Web Applikationen - bedcon13
Offline-Strategien für HTML5 Web Applikationen - bedcon13Stephan Hochdörfer
 
Testing untestable code - ConFoo13
Testing untestable code - ConFoo13Testing untestable code - ConFoo13
Testing untestable code - ConFoo13Stephan Hochdörfer
 
Offline strategies for HTML5 web applications - ConFoo13
Offline strategies for HTML5 web applications - ConFoo13Offline strategies for HTML5 web applications - ConFoo13
Offline strategies for HTML5 web applications - ConFoo13Stephan Hochdörfer
 
Offline-Strategien für HTML5Web Applikationen - WMMRN12
Offline-Strategien für HTML5Web Applikationen - WMMRN12Offline-Strategien für HTML5Web Applikationen - WMMRN12
Offline-Strategien für HTML5Web Applikationen - WMMRN12Stephan Hochdörfer
 
Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12Stephan Hochdörfer
 
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12Stephan Hochdörfer
 
Offline strategies for HTML5 web applications - pfCongres2012
Offline strategies for HTML5 web applications - pfCongres2012Offline strategies for HTML5 web applications - pfCongres2012
Offline strategies for HTML5 web applications - pfCongres2012Stephan Hochdörfer
 
Wie Software-Generatoren die Welt verändern können - Herbstcampus12
Wie Software-Generatoren die Welt verändern können - Herbstcampus12Wie Software-Generatoren die Welt verändern können - Herbstcampus12
Wie Software-Generatoren die Welt verändern können - Herbstcampus12Stephan Hochdörfer
 

More from Stephan Hochdörfer (20)

Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...
Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...
Offline. Na und? Strategien für offlinefähige Applikationen in HTML5 - Herbst...
 
Phing for power users - frOSCon8
Phing for power users - frOSCon8Phing for power users - frOSCon8
Phing for power users - frOSCon8
 
Offline strategies for HTML5 web applications - frOSCon8
Offline strategies for HTML5 web applications - frOSCon8Offline strategies for HTML5 web applications - frOSCon8
Offline strategies for HTML5 web applications - frOSCon8
 
Offline Strategies for HTML5 Web Applications - oscon13
Offline Strategies for HTML5 Web Applications - oscon13Offline Strategies for HTML5 Web Applications - oscon13
Offline Strategies for HTML5 Web Applications - oscon13
 
Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13Real World Dependency Injection - oscon13
Real World Dependency Injection - oscon13
 
Offline Strategien für HTML5 Web Applikationen - dwx13
Offline Strategien für HTML5 Web Applikationen - dwx13 Offline Strategien für HTML5 Web Applikationen - dwx13
Offline Strategien für HTML5 Web Applikationen - dwx13
 
Your Business. Your Language. Your Code - dpc13
Your Business. Your Language. Your Code - dpc13Your Business. Your Language. Your Code - dpc13
Your Business. Your Language. Your Code - dpc13
 
Phing for power users - dpc_uncon13
Phing for power users - dpc_uncon13Phing for power users - dpc_uncon13
Phing for power users - dpc_uncon13
 
Offline Strategies for HTML5 Web Applications - ipc13
Offline Strategies for HTML5 Web Applications - ipc13Offline Strategies for HTML5 Web Applications - ipc13
Offline Strategies for HTML5 Web Applications - ipc13
 
Offline-Strategien für HTML5 Web Applikationen - wmka
Offline-Strategien für HTML5 Web Applikationen - wmkaOffline-Strategien für HTML5 Web Applikationen - wmka
Offline-Strategien für HTML5 Web Applikationen - wmka
 
Offline-Strategien für HTML5 Web Applikationen - bedcon13
Offline-Strategien für HTML5 Web Applikationen - bedcon13Offline-Strategien für HTML5 Web Applikationen - bedcon13
Offline-Strategien für HTML5 Web Applikationen - bedcon13
 
Testing untestable code - ConFoo13
Testing untestable code - ConFoo13Testing untestable code - ConFoo13
Testing untestable code - ConFoo13
 
A Phing fairy tale - ConFoo13
A Phing fairy tale - ConFoo13A Phing fairy tale - ConFoo13
A Phing fairy tale - ConFoo13
 
Offline strategies for HTML5 web applications - ConFoo13
Offline strategies for HTML5 web applications - ConFoo13Offline strategies for HTML5 web applications - ConFoo13
Offline strategies for HTML5 web applications - ConFoo13
 
Offline-Strategien für HTML5Web Applikationen - WMMRN12
Offline-Strategien für HTML5Web Applikationen - WMMRN12Offline-Strategien für HTML5Web Applikationen - WMMRN12
Offline-Strategien für HTML5Web Applikationen - WMMRN12
 
Testing untestable code - IPC12
Testing untestable code - IPC12Testing untestable code - IPC12
Testing untestable code - IPC12
 
Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12Offline strategies for HTML5 web applications - IPC12
Offline strategies for HTML5 web applications - IPC12
 
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12
Große Systeme, lose Kopplung, Spaß bei der Arbeit! - WDC12
 
Offline strategies for HTML5 web applications - pfCongres2012
Offline strategies for HTML5 web applications - pfCongres2012Offline strategies for HTML5 web applications - pfCongres2012
Offline strategies for HTML5 web applications - pfCongres2012
 
Wie Software-Generatoren die Welt verändern können - Herbstcampus12
Wie Software-Generatoren die Welt verändern können - Herbstcampus12Wie Software-Generatoren die Welt verändern können - Herbstcampus12
Wie Software-Generatoren die Welt verändern können - Herbstcampus12
 

Recently uploaded

20230202 - Introduction to tis-py
20230202 - Introduction to tis-py20230202 - Introduction to tis-py
20230202 - Introduction to tis-pyJamie (Taka) Wang
 
Computer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsComputer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsSeth Reyes
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfinfogdgmi
 
Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024D Cloud Solutions
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfDianaGray10
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8DianaGray10
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintMahmoud Rabie
 
Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)Commit University
 
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostKubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostMatt Ray
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPathCommunity
 
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPA
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPAAnypoint Code Builder , Google Pub sub connector and MuleSoft RPA
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPAshyamraj55
 
Governance in SharePoint Premium:What's in the box?
Governance in SharePoint Premium:What's in the box?Governance in SharePoint Premium:What's in the box?
Governance in SharePoint Premium:What's in the box?Juan Carlos Gonzalez
 
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesAI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesMd Hossain Ali
 
OpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability AdventureOpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability AdventureEric D. Schabell
 
Machine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfMachine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfAijun Zhang
 
The Kubernetes Gateway API and its role in Cloud Native API Management
The Kubernetes Gateway API and its role in Cloud Native API ManagementThe Kubernetes Gateway API and its role in Cloud Native API Management
The Kubernetes Gateway API and its role in Cloud Native API ManagementNuwan Dias
 
100+ ChatGPT Prompts for SEO Optimization
100+ ChatGPT Prompts for SEO Optimization100+ ChatGPT Prompts for SEO Optimization
100+ ChatGPT Prompts for SEO Optimizationarrow10202532yuvraj
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxMatsuo Lab
 
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UbiTrack UK
 
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"DianaGray10
 

Recently uploaded (20)

20230202 - Introduction to tis-py
20230202 - Introduction to tis-py20230202 - Introduction to tis-py
20230202 - Introduction to tis-py
 
Computer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and HazardsComputer 10: Lesson 10 - Online Crimes and Hazards
Computer 10: Lesson 10 - Online Crimes and Hazards
 
Videogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdfVideogame localization & technology_ how to enhance the power of translation.pdf
Videogame localization & technology_ how to enhance the power of translation.pdf
 
Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024Artificial Intelligence & SEO Trends for 2024
Artificial Intelligence & SEO Trends for 2024
 
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdfUiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
UiPath Solutions Management Preview - Northern CA Chapter - March 22.pdf
 
UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8UiPath Studio Web workshop series - Day 8
UiPath Studio Web workshop series - Day 8
 
Empowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership BlueprintEmpowering Africa's Next Generation: The AI Leadership Blueprint
Empowering Africa's Next Generation: The AI Leadership Blueprint
 
Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)Crea il tuo assistente AI con lo Stregatto (open source python framework)
Crea il tuo assistente AI con lo Stregatto (open source python framework)
 
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCostKubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
KubeConEU24-Monitoring Kubernetes and Cloud Spend with OpenCost
 
UiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation DevelopersUiPath Community: AI for UiPath Automation Developers
UiPath Community: AI for UiPath Automation Developers
 
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPA
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPAAnypoint Code Builder , Google Pub sub connector and MuleSoft RPA
Anypoint Code Builder , Google Pub sub connector and MuleSoft RPA
 
Governance in SharePoint Premium:What's in the box?
Governance in SharePoint Premium:What's in the box?Governance in SharePoint Premium:What's in the box?
Governance in SharePoint Premium:What's in the box?
 
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just MinutesAI Fame Rush Review – Virtual Influencer Creation In Just Minutes
AI Fame Rush Review – Virtual Influencer Creation In Just Minutes
 
OpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability AdventureOpenShift Commons Paris - Choose Your Own Observability Adventure
OpenShift Commons Paris - Choose Your Own Observability Adventure
 
Machine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdfMachine Learning Model Validation (Aijun Zhang 2024).pdf
Machine Learning Model Validation (Aijun Zhang 2024).pdf
 
The Kubernetes Gateway API and its role in Cloud Native API Management
The Kubernetes Gateway API and its role in Cloud Native API ManagementThe Kubernetes Gateway API and its role in Cloud Native API Management
The Kubernetes Gateway API and its role in Cloud Native API Management
 
100+ ChatGPT Prompts for SEO Optimization
100+ ChatGPT Prompts for SEO Optimization100+ ChatGPT Prompts for SEO Optimization
100+ ChatGPT Prompts for SEO Optimization
 
Introduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptxIntroduction to Matsuo Laboratory (ENG).pptx
Introduction to Matsuo Laboratory (ENG).pptx
 
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
UWB Technology for Enhanced Indoor and Outdoor Positioning in Physiological M...
 
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"
UiPath Clipboard AI: "A TIME Magazine Best Invention of 2023 Unveiled"
 

How to build customizable multitenant web applications - PHPBNL11

  • 1. How to build customizable multitenant web applications Stephan Hochdörfer, bitExpert AG "Building an application so customizable it's the last application you'll ever need to build" Harrie Verveer
  • 2. About me  Stephan Hochdörfer, bitExpert AG  Department Manager Research Labs  enjoying PHP since 1999  S.Hochdoerfer@bitExpert.de  @shochdoerfer
  • 4. Developer vs. Businessman
  • 5. Single Tenancy – more customers
  • 6. Single Tenancy – even more customers
  • 7. Where will this lead to?
  • 9. Single Tenancy Tenant 1 Application Database Hardware
  • 10. Single Tenancy Tenant 1 Tenant 2 Tenant 3 Application Application Application Database Database Database Hardware Hardware Hardware
  • 11. Multi Tenancy Tenant 1 Tenant 2 Tenant 3 Application Database Hardware
  • 12. What should be customizable?
  • 13. What should be customizable? Tenant 1 Tenant 2 Tenant 3 Application Database Hardware
  • 14. What should be customizable? Tenant 1 Tenant 2 Tenant 3 Application Database Hardware
  • 15. Frontend | Branding How to skin an application?
  • 16. Application | Frontend How to skin an application? Remember: It`s a web application!
  • 17. Application | Frontend How to skin an application? HTML
  • 18. Application | Frontend How to skin an application? HTML + CSS
  • 22. Application | Frontend How to customize? <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>My App</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="stylesheet" type="text/css" href="css/styles/myapp.css" /> </head> <body> </body> </html>
  • 23. Application | Frontend How to customize? <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>My App</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="stylesheet" type="text/css" href="css/styles/<?php echo $tenant ?>.css" /> </head> <body> </body> </html>
  • 24. Application | Frontend How to customize? <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>My App</title> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <link rel="stylesheet" type="text/css" href="css/styles/myapp.css" /> <link rel="stylesheet" type="text/css" href="css/styles/<?php echo $tenant ? >.css" /> </head> <body> </body> </html>
  • 25. Application | Frontend Feature driven CSS Wait, there`s more...
  • 26. Application | Frontend Feature driven CSS display: none
  • 27. Application | Frontend Next level...
  • 28. Application | Backend Menubar generation <?php if($user->hasEnabled(Module::ORDERMANAGEMENT)) { if($user->canAccess(OrderManagement::LIST_ORDERS)) { $this->renderLink(OrderManagement::LIST_ORDERS); } if($user->canAccess(OrderManagement::ADD_ORDER)) { $this->renderLink(OrderManagement::ADD_ORDER); } if($user->canAccess(OrderManagement::CANCEL_ORDER)) { $this->renderLink(OrderManagement::CANCEL_ORDER); } }
  • 29. Application | Backend Menubar generation <?php if($tenant->hasModule(Module::ORDERMANAGEMENT) { if($user->hasEnabled(Module::ORDERMANAGEMENT)) { if($user->canAccess(OrderManagement::LIST_ORDERS)) { $this->renderLink(OrderManagement::LIST_ORDERS); } if($user->canAccess(OrderManagement::ADD_ORDER)) { $this->renderLink(OrderManagement::ADD_ORDER); } } }
  • 30. Application | Backend Menubar generation Modularize!
  • 31. Application | Backend Menubar generation Module 1 Module 2 Module 3 register at start up Application core
  • 32. Application | Backend Menubar generation Configuration for tenant 1 Module 1 Module 2 Module 3 register at start up Application core
  • 33. Application | Backend Menubar generation Configuration for tenant 2 Module 1 Module 2 Module 3 register at start up Application core
  • 34. Application | Backend Optimize workflows <?php if('CC' == $paymentType) { // handle credit card payment } else if('COD' == $paymentType) { // handle cash on delivery payment }
  • 35. Application | Backend Optimize workflows <?php if('CC' == $paymentType) { // handle credit card payment for some tenants! if(in_array($tenant->getName(), array('tenant1', 'tenant2')) { // insert logic here... } } else if('COD' == $paymentType) { // handle cash on delivery payment for some tenants! if(in_array($tenant->getName(), array('tenant3')) { // insert logic here... } }
  • 36. Application | Backend Optimize workflows Decouple functionality!
  • 37. Application | Backend Optimize workflows <?php $paymentType = 'CC'; // set via request $payment = PaymentFactory::create($paymentType); $payment->execute($order);
  • 38. Application | Backend Optimize workflows <?php $paymentType = 'CC'; // set via request $payment = PaymentFactory::create($paymentType, $tenant); $payment->execute($order);
  • 39. Application | Backend Optimize workflows How to add custom logic?
  • 40. Application | Backend Custom logic - Subclassing? Abstract Payment CC Payment CCPayment CCPayment Tenant 1 Tenant 2
  • 41. Application | Backend Custom logic Any alternatives?
  • 42. Application | Backend Custom logic Let`s add hooks...
  • 43. Application | Backend Custom logic - Hooks <?php $paymentType = 'CC'; // set via request $payment = PaymentFactory::create($paymentType, $tenant); if($this->paymentPreProcessor instanceof IPaymentPreProcessor) { $this->paymentPreProcessor->run($payment, $tenant, $order); } $payment->execute($order); if($this->paymentPostProcessor instanceof IPaymentPostProcessor) { $this->paymentPostProcessor->run($payment, $tenant, $order); }
  • 44. Application | Backend Custom logic How to set the dependencies?
  • 45. Application | Backend Custom logic Inject the dependencies!
  • 46. Application | Backend Custom logic – Dependency Injection <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.bitexpert.de/schema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.bitexpert.de/schema/ http://www.bitexpert.de/schema/bitFramework-beans.xsd"> <bean id="MyApp.Service.Order" class="MyAppServiceOrder.php"> </bean> <bean id="Tenant1.Service.Order" class="MyAppServiceOrder.php"> <property name="paymentPreProcessor" ref="Tentant1.Payment.PaymentValidation" /> </bean> <bean id="Tenant2.Service.Order" class="MyAppServiceOrder.php"> <property name="paymentPreProcessor" ref="Tentant2.Payment.StrictValidation" /> <property name="paymentPostProcessor" ref="Tentant2.Payment.PushOrderToSAP" /> </bean> </beans>
  • 47. Application | Backend Custom logic Any improvements?
  • 48. Application | Backend Custom logic <?php $paymentType = 'CC'; // set via request $payment = PaymentFactory::create($paymentType, $tenant); if($this->paymentPreProcessor instanceof IPaymentPreProcessor) { $this->paymentPreProcessor->run($payment, $tenant, $order); } $payment->execute($order); if($this->paymentPostProcessor instanceof IPaymentPostProcessor) { $this->paymentPostProcessor->run($payment, $tenant, $order); }
  • 49. Application | Backend Custom logic <?php $paymentType = 'CC'; // set via request $payment = PaymentFactory::create($paymentType, $tenant); if($this->paymentPreProcessor instanceof IPaymentPreProcessor) { $this->paymentPreProcessor->run($payment, $tenant, $order); } $payment->execute($order); if($this->paymentPostProcessor instanceof IPaymentPostProcessor) { $this->paymentPostProcessor->run($payment, $tenant, $order); }
  • 50. Application | Backend Custom logic Aspect-oriented programming
  • 51. Application | Backend Custom logic – Aspects for the masses! /** * @aspect */ class CustomPaymentProcessingAspect { /** * @around MyAppServiceOrder->processPayment */ public function customFilter(AOPJoinPointInterface $joinPoint) { // @TODO: implement pre-processing logic // ... $result = $joinPoint->getAdviceChain()->proceed($joinPoint); // @TODO: implement post-processing logic // ... return $result; } }
  • 52. Application | Backend Custom logic - Result <?php $paymentType = 'CC'; // set via request $payment = PaymentFactory::create($paymentType, $tenant); $payment->execute($order);
  • 53. Application | Backend Next level...
  • 54. Database Database – Where to store the data?
  • 55. Database Database – Where to store the data? We need to store data for a tenant!
  • 56. Database Database – Where to store the data? Database per Tenant?
  • 57. Database Database – Where to store the data? Database per Tenant? Schema per Tenant?
  • 58. Database Database – Where to store the data? Database per Tenant? Schema per Tenant? Tenant Id per Row?
  • 59. Database Database – How to access the data? vs. ORM dynamic statements
  • 60. What`s beyond? Generalize you should!
  • 61. What`s beyond? Multi Tenancy Software system family
  • 62. What`s beyond? No single solution!
  • 63. What`s beyond? A factory for mass production!
  • 64. What`s beyond? Multi Tenancy – Single Instance Tenant 1 Tenant 2 Tenant 3 Application Database Hardware
  • 65. What`s beyond? Multi Tenancy – Multi Instance Tenant 1 Tenant 2 Tenant 3 Application Database Hardware
  • 66. What`s beyond? Generative Programming Configuration Configuration 1 ... n Implementation Implementation Generator Product components Generator Product components Generator Generator application application
  • 67. What`s beyond? Generative Programming Configuration Configuration Tenant 1 Tenant 1 Implementation Implementation Generator components Generator components Tenant x Tenant x Generator Generator application application
  • 68. What`s beyond? Generative Programming - Goal Create an optimized application!
  • 69. What`s beyond? Generative Programming - Goal Create an optimized application for one tenant!
  • 70. What`s beyond? Generative Programming – Bonus points Reduce application complexity
  • 71. What`s beyond? Generative Programming – Bonus points FileFrm FILEOrderService_php5 { private String PreProcessor = ""; private String PostProcessor = ""; public FILEOrderService_php5() { setFilename("Order.php5"); setRelativePath("/classes/MyApp/Service"); } private void assign() { BEGINCONTENT() <?php $paymentType = 'CC'; // set via request $payment = PaymentFactory::create($paymentType, $tenant); <!{PreProcessor}!> $payment->execute($order); <!{PostProcessor}!> ENDCONTENT() } }
  • 72. What`s beyond? Generative Programming – Bonus points FileFrm FILEOrderService_php5 { [...] private void configure() { if(this.getConfiguration().hasFeature('PreProcessor')) { PreProcessor = this.getPreProcessorContent( this.getConfiguration.getTenant() ); } if(this.getConfiguration().hasFeature('PostProcessor')) { PostProcessor = this.getPostProcessorContent( this.getConfiguration.getTenant() ); } } }
  • 73. Application | Backend Generative Programming – Bonus points Example: Preprocessor: Postprocessor: Output:
  • 74. Application | Backend Generative Programming – Bonus points Example: Preprocessor: Postprocessor: Output: <?php $paymentType = 'CC'; // set via request $payment = PaymentFactory::create($paymentType); $payment->execute($order);
  • 75. Application | Backend Generative Programming – Bonus points Example: Preprocessor: $this->paymentPreProcessor->run($payment, $tenant, $order); Postprocessor: Output:
  • 76. Application | Backend Generative Programming – Bonus points Example: Preprocessor: $this->paymentPreProcessor->run($payment, $tenant, $order); Postprocessor: Output: <?php $paymentType = 'CC'; // set via request $payment = PaymentFactory::create($paymentType); $this->paymentPreProcessor->run($payment, $tenant, $order); $payment->execute($order);
  • 77. What`s beyond? Generative Programming – Bonus points Reduce maintenance support
  • 78. What`s beyond? Generative Programming – Bonus points Implementation Feature component
  • 79. What`s beyond? Generative Programming – Bonus points Feature Implementation component
  • 80. What`s beyond? Generative Programming – Bonus points Customer Feature Implementation component
  • 81. What`s beyond? Generative Programming – The book