For a long time, Meteor has been offering the possibility to build very large, truly modular applications with its unique packaging system. With ES6 modules, dynamic imports, and separated server and client code inside packages, there is almost no limit to the modularity and pluggability which can be achieved in a Meteor app.
The architecture envisaged and proposed by @sacha in Vulcan, and which depends on the packaging system, is a truly unique way of building apps. In our view, it trumps micro-services when it comes to large applications. For a very simple visual of what one can do with this architecture, have a look at micro-frontends, under Organisation in verticals. Just think of the possibilities!
It would be a real shame if this ability was taken away for good. We looked a while back at using NPM packages as a replacement for Meteor packages in this architecture and it’s just not the same. For complex Meteor apps (I would say complex apps generally), the package based architecture is one really neat solution.
That is exactly what we do also, but after the pull request mentioned in my original post, a client-only code change triggers a server rebuild. Hence my complaint that it makes development harder.
The behaviour is described in detail in this issue.
For example, if we get this right, and you change a file in your application’s imports/ directory that we know is only used on the client, even though it’s not in the client/ directory, Meteor should be able to do a client-only refresh (without restarting the server). The same logic would apply to packages.
In general, I want to move away from magic directories and use the information that’s already embedded in the module dependency graph.
After this is all sorted out, it would be nice for a comprehensive explanation of how to structure our applications again. I’m on Meteor 1.8 and still have client/server directories (and make little use of the imports directory).
The application is on Blaze, but I’m migrating to React now. Amazing Meteor 1.8 still works with my code from years ago by the way.
I’d like to jump to a situation where I don’t have to use an imports directory. I don’t see what’s so wrong with having a server/client/both directory and inside each making everything modular with imports like is the case using a standard bundler like Webpack for example.
I don’t use a “Packaging” system as some rave about, I don’t see what the big deal is and why some are adding another level of abstraction. Just to split the codebase up for farming out code chuncks for development? Are you guys making a bunch of small projects and just linking them into a “greater” meteor project for bundling? If so, seems overkill.
If you move to micro services, usually they’re totall separate, could stand alone, chunck of functionality that “talk” via a web API or the like. I’d like someone to explain what micro services mean in the Meteor context, are you building separate micro services “Packages” that communicate via Meteor Methods?
In my case, i am using packaging system because my client code base is web and cordova in the same time, so i separate each logic in a different package to avoid having web code in cordova ( mobile ) app and vice versa.
I have an app that still uses Blaze, and it still uses the magic folders, with eager loading, and all that.
My more modern react apps all mostly use the imports directory, though I still find it useful to place my entry points and other configuration in the client and server folders. I had played with moving everything to root, but that got messy, and decided to simply leave everything in classic folders, and it’s pretty clean that way. Inside the imports directory things are organized like many react apps - api, ui, and utils folders at the base, and then appropriate things where they go, etc.
Occasionally, I do create a local package. This is useful for example if you want to have a single import location, which works for both client rendering and SSR, but want to provide different API compatible libraries for client and server. I do this in my data tier, which is offline first, and built on top of Meteor methods, to bypass the overhead of pub/sub (which I only use sparingly for cases where I do need realtime). But most stuff lives cleanly in the imports directory.
Hey. I’ve just realized that inside a package I can import files from the server folder on the client with no issues. Do I understand correctly that there is no way to prevent such accidental imports of server-only code now?
That is correct; besides you can name those directories any way you like. At this point, I think it’s mostly just a convention (and good practice) to name them like that, because Meteor uses modules nowadays as opposed to relying on folder naming. You could also have a common folder from where you import in both, should you need to do so (e.g. when defining collections).
The most elegant way is to use imports, and a modular package structure, which prevents this type of accidental error. Also good for maintainability.
Thank you for the quick reply. I do use a modular package structure but it doesn’t seem to solve the issue. Specified entry points still don’t prevent doing something like this:
// server.js
export const a = 'a'
// client.js
import { a } from './server'
Or more real-life example:
// client.js
import { serverOnlyFn } from './common/api/{module}/server'
It’s really easy to overlook some nested import of the server code and load it to the client.
These recommendations becomes totally useless. It’s now impossible to import secret server code with require() in method body as all require statements are proceed on the client.
The recommendations still work for main app code, so it’s by no means totally useless
I get around this change by sticking to the folders as a convention, which makes it easier to see that a file should be server or client only. You could also make an eslint rule for it if you wanted
Thanks! I meant totally useless for packages of course. I’ve also discovered that @meteorjs/eslint-config-meteor can help but only in case of direct imports of files with server in the path by files with client in the path and vice versa.