Hints for refactorability


#1

After years of java, I find javascript a breath of fresh air and I kind of enjoy moving away from object oriented to functional.

But for larger applications, especially with everchanging collection schemas / models, where there are lots of collections with multiple references to other collections and strong referential data integrity requirements - I’m perfectly fine with mongodb, let’s not get into a sql/nosql argument; I feel like I am painting myself into a corner, especially when I don’t use explicit getters and instead directly access document properties such that when a property gets changed it becomes very hard to refactor that to meet the new model requirements.

Using the wonderful webstorm does not help much there either and having to resort to cumbersome find/replace/test/fix is not much fun.

And that (getters) is not the only thing I keep finding my self missing or subconstiously implementing.

I resort to taking cues from ideas (that I’m already trying to abandon) like getters/setters, data access objects, interfaces, centralize queries etc.

I even found myself reading a typescript tutorial at some point today! Wow! just like emotional eating :smile:

And this is just the data layer. Other layers of the app seem to require (although to a lesser extent) similar object-orientification.

The thing is, I only seem to want that for easy refactoring paths. Other than that, I’m perfectly fine with creating and composing functions. Heck, I’m even fine with procedures since I’m blessed with significant pascal and fortran experience LOL :smile:

So, what do you guys do to keep sane refactoring paths, especially with schema changes.


#2

I am not sure I can help you, but recently I am using for every route/view/layout kinda 1 big flux-like template in controller level where I manage routing of all data to dumb components which needs them. Wiring it this way. https://www.discovermeteor.com/blog/meteor-components-template-controller-pattern/

So far it worked OK, maybe I will even add Blaze+, but I kinda learned how to prevent re-renders in Iron router universe. Now with flow router it is like summer all year long :smiley:

But in the recent weeks I am finding myself looking on all these easy automatic publish functions, dreaming about easy switch to make them non-reactive. I would be so glad if I can just set reactive:false there so I can use nice mongodb queries on client side and not have to rewrite all publications to low level API.
And aggregations on minimongo would help too :smiley:


#3

I’m not sure I can help either, but I will say that I use jagi:astronomy, meteorflux:dispatcher, and meteorflux:appstate in all my apps now. I find I have fewer headaches now. :slight_smile:


#4

@ffxsam I’ve been eyeing astronomy for a while now and I’ll definitely be using it in my next largish project which is about to start any day now.

I hadn’t given any thought to the meteorflux packages but the idea of keeping a central state and a dispatcher sounds like a nice way to narrow down points of refactoring into some central place. It’s like having a meta interface implementation to everything else. But I’m also reluctant because the kind of apps I work with have many many many collections with intertwined joins so a central state object would grow very large raising performance concerns. Or do I need not worry?

@shock thank you as well for chiming in. It is interesting that you also have brought up a flux-like architecture.

I think Sacha’s pattern is nice if one wants to create a component tree that share common properties. That’s not directly related to my initial concern of easy refactoring paths, but sure is a nice approach for when the ui also grows with repeating pieces of code.

Regarding non-reactive, I have not actullay yet been bitten by it much. I’ve only needed non-reactive for performance concerns on subscriptions to low-velocity data.


#5

I tend to use multiple stores (Flux) rather than a single one (Redux). It just works better for me personally. So I’ll have a store for, say, a page that shows audio files and a background activity (uploader), which contains all the stuff related to that.

AppState.set('audio', {
  showBgActivity: false,
  uploadQueue: [],
  // ...
});

// I'm not a fan of switch statements
Actions = {
  'USER_TOGGLED_BG_ACTIVITY': function () {
    // ...
  }
};

Dispatcher.register(function (action) {
  Actions[action.type] && Actions[action.type](action);
});

Then in any component that needs to reference this stuff:

BackgroundActivity = React.createClass({
  mixins: [ReactMeteorData],

  getMeteorData() {
    return {
      bgActivityVisible: AppState.get('audio.showBgActivity')
    }
  }
  // ...

Huge fan of this. :slight_smile: It’s greatly simplified the structure of my code.

Anyway, I don’t know if any of that helps, but I thought I’d offer that up anyway.


#6

Thanks a lot. I’ve skimmed through the meteorflux package readme’s and it is nice to know that it is not a react specific implementation. I’m not yet sold on React.

The only downside I see that it is a view-oriented pattern. I’m looking for patterns I can use on all app layers, although I guess the flux/appstate idea can be implemented on the server as well.

Your example looks quite interesting, but the only part I find ugly in terms of refactorability is AppState.get('audio.showBgActivity') namely the use of 'audio.showBgActivity' string literal whereas I would have preferred an accessor method or a property chain.


#7

After experimenting many kind of patterns and styles, I’ve been doing the simplest thing on huge projects: namespacing. Just like you see on Meteor’s source code: they are simple, they work nicely and more important, people will understand right away when they look at your code.

MyFeature = {};
MyFeature.foo = function () {
//...
};

// use MyFeature inside OtherFeature
OtherFeature.bar = function () {
  var defaultFooler = MyFeature.foo();
};

You can go further if you want more decoupling and easy switch…make it a property of other namespaces:

OtherFeature = {
  myFeature: MyFeature
};

OtherFeature.myFeature.foo();

#8

Thanks @gabrielhpugliese this is exactly what I do to keep paths to my functions, in fact I have a “namespace” package that gets pulled in to every other package of my app and those packages extend the namespace instead of exporting variables.

So I end up with something like

App = {
  Colls: {},
  Utils: {},
  Libs: {}
}

and sometimes even further go down another level to separate modules or app interfaces. I find this a great practice, a little too verbose for coding, but I guess that would be resolved when official modules support come along.