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
 
[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
 
CQRS and Event Sourcing
CQRS and Event Sourcing CQRS and Event Sourcing
CQRS and Event Sourcing Inho Kang
 
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
 
[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 패턴 실습
 
CQRS and Event Sourcing
CQRS and Event Sourcing CQRS and Event Sourcing
CQRS and 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

🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024BookNet Canada
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhisoniya singh
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitecturePixlogix Infotech
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGSujit Pal
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)Gabriella Davis
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationRidwan Fadjar
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonAnna Loughnan Colquhoun
 

Recently uploaded (20)

🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
#StandardsGoals for 2024: What’s new for BISAC - Tech Forum 2024
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | DelhiFULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
FULL ENJOY 🔝 8264348440 🔝 Call Girls in Diplomatic Enclave | Delhi
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Understanding the Laravel MVC Architecture
Understanding the Laravel MVC ArchitectureUnderstanding the Laravel MVC Architecture
Understanding the Laravel MVC Architecture
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
Google AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAGGoogle AI Hackathon: LLM based Evaluator for RAG
Google AI Hackathon: LLM based Evaluator for RAG
 
A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)A Domino Admins Adventures (Engage 2024)
A Domino Admins Adventures (Engage 2024)
 
My Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 PresentationMy Hashitalk Indonesia April 2024 Presentation
My Hashitalk Indonesia April 2024 Presentation
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 

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