Best practices to develop SaaS in Meteor in 2017

There are threads for

2015 - Best way to architect a multi-tenant SaaS?
and
2016 - How can develope a SAAS application in meteor?

and there a lot of ideas in them but no uniform solution.

Are there any new tricks and tips for 2017?

Boilerplate, anyone?

A SaaS platform has many different shapes, sizes and use-cases. Like websites. Can you be a bit more specific about what your needs are? There are basically 2 main types that I would use with Meteor

  1. 1 replicaset database, 1 meteor application on 1 server. Good for companies with just a few customers that use the system occasionally. (cheapest, and easiest setup). This works in most low load cases. Keep in mind that preventing privacy accidents where customers can see other customerā€™s data is more present here. Also if using Meteorā€™s account system, 1 username must be unique across all customers
  2. A sharded database and 1 meteor application on multiple servers. Generally the way to go. Provides good separation and prevents load sharing between customers, but is more expensive in resource usage and is more complex to set up

@cloudspider, thank you for your suggestions and advice on this. I just wonder, is it ever appropriate to use more than one database? I mean, a database per customer?

Right now, I have it to where I have (1) Application and (1) Database per customer, and itā€™s working out fine. Iā€™m now working to break out a portion of the project into one central application for specific tasks that are longer running ā€“ yet still keep the multiple databases. I had thought about using one database, but have concerns about creating a bottle neck.

Hereā€™s where I ask for advice on one application connecting to multiple databases: Meteor Server-only Web App connecting to Multiple Databases Any advice is appriciated.

Ah I see. Yes it can become a big issue to have 1 database per customer and just 1 application for all customers. Its because of how Meteor (and other platforms that use sockets and in memory collections / caches) deal with collections. This can however easily be overcome by putting namespaces. My recommendation is always to use a slug form client abbreviation. For example: If Google would be your customer, its going to be google. You might want to abbreviate long names to prevent database limitations regarding character limit: Facebook becomes fb.

1 Database per customer is perfectly fine. The time spent is then usually on the DevOps side. For example if you want a customer to register itself it has to automatically generate that database and connection etc etc.

For as far as the bottleneck counts. Keep in mind that it should be possible to horizontally scale your Meteor app. If 1 customer requires a lot of resources, it might cause other customers to experience delays.

I think that you might want to stick to the 1 database and meteor app per customer approach. Just spent your time on continuous integration workflows! Automate the build and deployment steps of your app so that each application will be updated automatically after a commit / push to your repository. This will remove all complexity from the app side, because they become dedicated to 1 customer. That is unless you want to have some sort of global admin login functionality. However, thatā€™s still possible.

@cloudspider Iā€™m using similar approach (1 DB, multiple apps) but Iā€™m wondering if there will be issues while scaling the number of app instances as the connections and OPLOG tailing to the DB grows.

Any commants/experience with that?

I have no insights on how your application is used. Few users might load the database intensively based on how they use it how well queries can be reused by other users etc etc. Scaling the database should be fine. MongoDB is really good, if not build for that purpose. The oplog tailing is Meteor specific and might become an issue when you start to have a lot of changing data. To overcome that you can then still decide not to use the oplog for some collections and only for parts that really require realtime functionality like chat apps and auction apps.

1 Like

Could you explain this more, Iā€™m not sure I understand you. I have one application with one database per customer. Are you saying this is bad/wrong somehow?


Iā€™d like to add a worker service that runs on its own and does file processing and other long running tasks, therefore it needs access to all the customerā€™s dbs:

This is the part I need help with, I need to find a way to access multiple DBs from one app:

@cloudspider I would rather start with the first setup and proceed to second when there is some ā€˜tractionā€™.

My app is rather classic - multiple companies with multiple users using it to track their business transactions.

Why you consider setup #1 to be susceptible to privacy accidents? I do not think they may happen unless there is some error in the code.

You could also just use PeerDB that lets you describe relations which are then enforced from oplog, so that you could have schema record representing all of the keys or records per customer.

the idea that duplicating db or wtv is worse.

In terms of programming, my best practices for developing a SaaS MVP right now would be:

  • Meteor and the basic packages - aldeed:simple-schema, iron:router, aldeed:tabular, accounts-base/ui/password, sacha:spin, react-template-helper, less/sass
  • Semantic UI or Bootstrap
  • Blaze as data container templates that use react-template-helper to load React components with Meteor data via helpers
  • React - Container/component architecture - containers manage state, call Meteor methods, and pass props to (usually) functional components. Example: class MessageListContainer extends Component vs const Message = function (props) {}
  • Services architecture - Domain objects are broken into at least one service object each. Service objects contain methods for business logic broken into small, often re-useable functions. Meteor methods, cron, migrations, and publications all call service object methods. Example names: MessageService, MessageMigrationService, UserService
  • /client/imports/components, containers, routes, services, templates folders
  • /imports/collections, constants, services, schemas, tables (<- tabular)
  • /lib/methods
  • /server/imports/publications, services, cron
2 Likes

Hi dancastellon,

Thank you for your best practices ! I like the way you structure the files and folders

What do you put in /lib/methods, Meteor methods ? Why you donā€™t put them in server folder for security side ?

The reason is that methods can also run on the client-side, for optimistic UI updates. I would recommend having 2 separate method folders: one for server-only methods and one for isomorphic methods. But Iā€™m using local packages anyways, so my app folders are usually quite empty.

Thanks @waldgeist

What is the advantage of a local package over a folder that contains all the logic (with ā€œexportā€ of what we want to be visible ) ?

Well, one one hand, it forces you to clearly modularize your app, so everything that belongs to a certain domain (including assets and such) becomes a separate package. Of course, this could also be achieved by using ES6 modules and a folder structure designed for this domain separation.

The biggest advantage, however, is that you can re-use such packages across several apps, by putting them into a special folder referenced by METEOR_PACKAGE_DIRS. You need some functionality like content rating? Just include the package youā€™ve already developed for another app, without copying the code around or using private NPM packages, This is the main reason why I absolutely love the Meteor package concept.

One third reason is that only packages let you define clearly which code becomes part of a mobile app and which become code part of a web app. Without packages, you would have to use isCordova() and conditional includes a lot. Packages automate this task. This is something Node packages are dearly missing.

3 Likes

You can just use symlinks with importsā€¦ we recently moved 60+ internal packages used across 5 apps as imports and are not looking back.

2 Likes

I donā€™t really think anyone can give you a best practices answer with boilerplate.

Either, SaaS involves tons of back-end scalability, because a true SaaS application has the provider handling literally everything behind the scenes. This means that you need to be prepared to host ALL your customersā€™ data, be responsible for redundancy, global availability, performance of everything. Itā€™s not a cheap or easy endeavour, would likely take months or years of work, so I donā€™t feel that any amount boilerplate could prepare you for that. Itā€™s also just not within the scope of any framework, whether itā€™s Meteor or Apollo or anything to handle that for you. You need hardware and serious back-end/database skills from a deployment and administrative standpoint. Itā€™s not really about just being a good web dev.

Thatā€™s my two cents, anyway.

2 Likes

Well, youā€™re talking about two different things.

  1. Using just one database instance would, indeed, create a bottleneck which is why the common practice with MongoDB is to create replica-sets and sharded instances across multiple servers, of the same database however.
  2. However, the way it sounds like youā€™ve got it set up now is youā€™re having your customer manage their own database instances that they provide credentials for your app to connect to? Thatā€™s more of a hybrid SaaS instead of a pure SaaS app where you handle everything. Itā€™s a good compromise for customers that want to administer their own data as opposed to leaving it to you. Having a separate database per customer that you manage yourself doesnā€™t seem that appropriate to me, tbh. You just need to have a data-model (i.e. stay away from big sub-documents and multiple nested objects, and use reference collections instead) that is properly indexable and efficiently searchable so that customers donā€™t need to wait forever for their data.

But the original point still stands, you should shard your db and distribute replica sets across multiple servers, as well as instances of your app itself, around the world so that your customers have a good experience with the service no matter where they are.

2 Likes