Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Building a Multi-tenanted SaaS with Node.js


Published on

Using, Node.js Seneca and MongoDB to build a multi-tenanted SaaS with dynamically-provisioned, isolated databases for each tenant.

Published in: Technology

Building a Multi-tenanted SaaS with Node.js

  1. 1. 111 Steps to Building a Multi-tenanted SaaS in Node.js Eoin Shanaghy Edappy eoins eoinsha 1
  2. 2. Edappy started with a single-tenanted internal system, delivering University-level courses Node.js - Seneca - Express - MongoDB Microservices Dockerised EC2 The job: Make the platform a multi-tenanted SaaS
  3. 3. The Beginning 3
  4. 4. “This should be easy” 4
  5. 5. We started to think about Tenant Provisioning. “How often?” “How big?” “How does it relate to the business model?” “How will we…monitor it, maintain it or deploy it?” 5
  6. 6. We will have many small tenants. Some will be just evaluating, at least at first. They will be spread internationally and many will use a free tier. A few will be large, enterprise tenants. They will take longer to publish their content but will have many thousands of students and many, large courses. 6
  7. 7. Don’t just think production. How do I run a multi-tenanted SaaS in development? What will my tests look like? How many processes, containers and VMs will I need to run? 7
  8. 8. First, share. Duplicating anything increases overhead. Share resources where possible. Isolate only when required. 8
  9. 9. No new hardware. Before adding any containers or instances, maximise every bit of existing infrastructure. Faster start. Lower maintenance. More cost effective. 9
  10. 10. 10
  11. 11. Actually, one new container 11
  12. 12. Isolation There are three ways to isolate tenants’ data. Document/table per tenant Collection/Table per tenant Database per tenant 12
  13. 13. 13 HTTP - Express - Seneca - Actor - MongoDB
  14. 14. 14
  15. 15. 15 $> mongo test --eval “shellPrint(db.person.find())" { "_id" : ObjectId("56563ed8f25f27f90cedeafe"), "name" : "Fred", "age" : 17 } $> mongo test2 --eval “shellPrint(db.person.find())" { "_id" : ObjectId("56563ed8f25f27f90cedeaff"), "name" : "Elizabeth", "age" : 27 }
  16. 16. Seneca already gives you: routing pattern-based segregation/partitioning The explicit version: 16
  17. 17. Transparent, in-band context Within any actor, I want to know which tenant domain initiated the request. 17
  18. 18. 18
  19. 19. 19
  20. 20. 20
  21. 21. So we wrote… 21 seneca-context allows you to set or get any data relating to a single request at any point in the action chain, even across transport boundaries
  22. 22. 22 Express seneca-context seneca-web createContext Tenant Registry action action action transport boundary POST /api/user/auth Host: D D D D “d”? seneca-web plugin
  23. 23. DNS Route53 Wildcard subdomains first: * * * - Programatically registered thereafter 23
  24. 24. Tenant Stores There are hundreds of actors Each actor has 0..* store operations How do you avoid explicitly setting the zone for every operation? 24
  25. 25. Write another Seneca plugin Use seneca.wrap Intercept all entity actions Look up the zone/context if not already set Provision the DB, if not already done Configure the store plugin Set the zone and call seneca.prior 25
  26. 26. Now… 26
  27. 27. Was… 27
  28. 28. Other parts of the story 28
  29. 29. Hurdles Don’t create or read any tenant-specific data on init 29
  30. 30. Tenant Provisioning Register a tenant record Create external resources (bucket) Configure database Approximately $0 cost 30
  31. 31. Integration Testing At least one instance of each service At least two tenants Clean database per suite Docker Compose Each test begins outside the app boundary (SuperTest) Seneca test actions to perform setup/teardown 31
  32. 32. Oversight We use ELK for aggregated logs across containers and services Log and index tenant ID APIs Actions Debugging 32
  33. 33. Scaling Up to a point, adding instances, containers is okay Large, high value tenants warrant dedicated resources Limit tenant-specific resources per app instance (DB connections, etc.) Explicit/Manual, Mesos, Kubernetes 33