Mantra: Are isomorphic methods considered bad?

I know one person. He is very good with algorithms but a newcomer on frontend. He could easily do that mistake, because he is not interested in get deepen. He takes snippets and build things. It’s the same story for many other fields, for example, people that do stuff with IoT. Mostly, they want to connect the hardware parts carefully but when it comes to the software, they do just to work as fast as they can.
It’s not hard to move sensitive logic into the server folder, but it can be late if your app is online enough time to someone get the info.
Just my two cents :slight_smile:

Working “as fast as possible” and building secure apps is impossible. If you want to build something as fast as possible, you read only enough documentation to be able to do what you need. If you want to build a system that’s really secure, you must read the documentation and understand what your code does.

Personally, I think having optimistic UI (aka latency compensation) in Meteor (which isomorphic methods enable) is 1000 times worth the price of having to read the docs and realize that you have to put code with sensitive information (not sensitive logic, mind you!) into the server directory. If you don’t care about security, you might get by with not reading the docs, but if you care about security, you’ll have to read the docs anyway, regardless of whether Meteor’s methods are isomorphic or not!

This is where CQRS principles can help, as the UI can only make state changing actions by sending a command the system exposes. It’s similar in spirit to what we get with using GraphQL to manage the query interaction. Since these command objects are sent from task-based UIs and are type-checked , it’s still very reasonable to predict the outcome and do latency compensation. The server-side API just needs to validate the client is authorised to indeed make that action.

Isn’t this exactly how meteor methods work?

1 Like

Yes exactly! The key point is not the infrastructure but the design -> commands should be ack/nak only, and not expect a result. This makes optimistic updates easier to reason about

In Space Meteor methods are the way we achieve the command pipeline from client to the domain (with latency compensation). Our Api object is a thin wrapper that maps the name of a command to a Meteor method name, only accepting that object. You can of course do this without the Api wrapper, so if not using Space modules for DI and other features it’s just a convenience.

Example from Todos

1 Like

I know I am late to this party, but couldn’t you still write a somewhat isomorphic method that uses server-side only external calls to do the business logic?

I find, for the most part, that 95% of the methods I am writing are literal and exact copies between client/server. For the 5% that I need to do something crazy, and I want to hide it, I can either encapsulate that into a separate server-only method, and still use isometric for most of the method, or it’s something where I can live without simulating on the client all together, so isometric isn’t really an issue.

That said, I am trying to adopt the “Mantra” way 100%, so I am creating my methods in both places. But, from a maintenance standpoint I can see this will be a challenge to maintain in larger projects. And, this duplication of code seems to be completely against the DRY mentality.

1 Like

I’ve solved this by adding MDG validated methods to the /lib/collections/xxx.js files like this:

Items.methods = {};
Items.methods.insert = new ValidatedMethod({
  // ...
});

For server-only stuffs I’ve created /server/method_helpers/xxx and include them like this:

Items.methods.secretStuff = new ValidatedMethod({
  name: 'Items.methods.secretStuff',
  mixins: [CallPromiseMixin],
  validate: null,
  run() {
    if (!Meteor.isServer) return false;
    const Future = require('fibers/future');
    const fut = new Future();
    const { doSomethingAsync } = require('/server/method_helpers/items');
    doSomethingAsync.then(res => fut.return(res)).catch(res => fut.throw(res));
    return fut.wait();
  },
});

Then I call methods with Items.methods.secretStuff.callPromise() which returns a promise.

2 Likes

first of all, I’ve learned A LOT since I wrote this, and would like to add that the “isomorphism” terminology should probably be replaced with “universalism”, as in “universal javascript” :smile: Not to be rude, but just to spread the word :sunny:

yup, for edge-cases where you need a mix of simulated and private logic, you could use remote calls. I would however do the split on the client, and have the private stuff and the simulated stuff separated fully, and have the client use them separately. I’ve done this frequently, and I bet you have too? :wink:

I couldn’t agree more - I would be afraid to duplicate like this. I don’t know much about Mantra, and can’t really speak to what they offer in terms of optimistic UI, but they must have a better model than duplication.

1 Like

In my opinion, they don’t.

I looked at Mantra with all seriousness for a couple of days. I then decided that it wasn’t for me. The attempt to pretend that server and client are “unrelated” was in my opinion a bad move. Meteor Methods and Meteor Publications are for all intents and purposes the contract of loosely coupled code. Where you need private code, have a noop version for the client. Then both server and client can call into that method, but on the client, nothing happens. The client can’t “predict” what will happen in the secure server code. But do all actual interface data changes in isomorphic functions to enable optimistic UI. For example, let’s say that on the server you want to check some kind of roles:

Meteor.methods({
	somethingWithRoles(newData) {
		if (rolesCheck() {
			someCollection.insert(newData);
		}
	}
});

// server.js
rolesCheck() {
	// test user, make sure they are valid
	// using Meteor.currentUser()
	return userIsValid;
}

// client.js
rolesCheck() {
	return true;
}

This code accomplishes optimistic UI out of the box while still keeping the roles logic “secret”

This has all the benefits of a split application, while still being easy to update later.

This code is not allowed by Mantra.

5 Likes

nonononononononononononononono :grin:

1 Like

Sorry, I meant “universal” I think? :wink:

1 Like

I feel like arguing about the terminology doesn’t help anyone. Just let people call it what they want! Sure, isomorphic as a mathematical term doesn’t really mean the same thing but whatever.

3 Likes

I love mantra but the question on how to implement methods in the more efficient way remains…