Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

features structure #8

Closed
dtwist opened this issue Mar 22, 2016 · 15 comments
Closed

features structure #8

dtwist opened this issue Mar 22, 2016 · 15 comments
Assignees

Comments

@dtwist
Copy link

dtwist commented Mar 22, 2016

First, thanks for this. Of all the 1.3/react/etc boilerplates folks are putting out, Mantra included, this is coming closest to an approach that resonates for me. I also think the notion of annotating the example branch inline is brilliant. Would love to see a more comprehensive version of such an approach!

In the Readme, you mention:

exploring the idea of putting all features into their own respective folders

The boilerplate includes an empty feature skeleton, but the example branch has no features. It looks like your approach evolved after branching the example off. Just curious what your initial thinking is here. Are you borrowing from Mantra's approach to file structure?

Lastly, any particular reason you eschewed the Meteor guide's recommendation of using a root import directory?

@ffxsam ffxsam self-assigned this Mar 23, 2016
@ffxsam
Copy link
Owner

ffxsam commented Mar 23, 2016

Hey David!

The boilerplate includes an empty feature skeleton, but the example branch has no features. It looks like your approach evolved after branching the example off. Just curious what your initial thinking is here. Are you borrowing from Mantra's approach to file structure?

Correct, the example code is not using the feature structure yet, as I'm still testing it in real-world applications before I decide if it's a flop or not. :) And no, I'm not borrowing from Mantra. I just had this idea that if I'm working on a large application, and Feature XYZ is giving me problems, it might be easier to dive into the code if everything related to XYZ is in /features/XYZ.

Lastly, any particular reason you eschewed the Meteor guide's recommendation of using a root import directory?

I feel it's a bridge as people who are used to the "globals everywhere" model of 1.2 move into 1.3 and get used to import/export. I have a feeling MDG will eventually make everything 100% explicit imports. Plus, my folder hierarchies are deep enough as it is, I didn't want to introduce yet another level!

@dtwist
Copy link
Author

dtwist commented Mar 24, 2016

Thanks for the clarifications.

Since I'm just getting into Meteor now (awesome timing, no? 😉 ), the magical, globals-everywhere approach feels way too loosey-goosey for me, so I rather liked that MDG is counseling avoiding it. I get the same feeling as you that they'll move towards an import-only approach in future versions.

And btw, didn't meant to suggest you were following Mantra's philosophical approach, just the abstract notion of modularizing features. I put some good time into exploring Mantra over the last couple weeks, and in the end it seems too constraining for the sort of projects I am entertaining with Meteor.

I am still a bit fuzzy on how the modular approach in your current—wip, I get it 😄—boilerplate will work with the separation between server and client; If the entire features directory is located in the root client folder, doesn't that mean all the server-specific code will get incorporated into the client as well? As a Meteor greenhorn, am I missing some important detail?

@ffxsam
Copy link
Owner

ffxsam commented Mar 24, 2016

Since I'm just getting into Meteor now (awesome timing, no? 😉 ),

Ha! "Pardon our dust.."

And btw, didn't meant to suggest you were following Mantra's philosophical approach, just the abstract notion of modularizing features.

Yeah, I think I understood that. I just meant that I didn't get the idea from them—just my own private brainstorming sessions. :)

My boilerplate, while evolving, can be considered complete and usable. But now I see what you're asking, about separation. The idea is to put client, lib, and server folders within features. E.g.:

features
├── Admin
│   ├── client
│   │   ├── components
│   │   │   ├── AddEditOrgDialog.js
│   │   │   ├── AddEditUserDialog.js
│   │   │   ├── OrgList.js
│   │   │   ├── OrgManager.js
│   │   │   ├── OrgManager.scss
│   │   │   ├── OrgMemberList.js
│   │   │   ├── OrgMemberList.scss
│   │   │   ├── OrgUserManager.js
│   │   │   ├── UserList.js
│   │   │   ├── UserManager.js
│   │   │   └── UserManager.scss
│   │   └── containers
│   │       ├── OrgManagerContainer.js
│   │       ├── OrgUserManagerContainer.js
│   │       └── UserManagerContainer.js
│   └── lib
│       ├── methods.js
│       ├── organisations.js
│       └── users.js
...

And certainly not all features will even use a lib or server folder, but the option is there. For example, I might have a /features/Billing/server folder to hold private Stripe-related stuff.

Hope that clears things up!

I've also moved away from JS styles to using BEM. IMO, JS styles fall apart when you want to share common stylings among several components. I started defining object literals in other places and importing them to access styles, and it felt clunky to me. I believe CSS Modules will eventually be the landing point for React, and I think using BEM will lead to a smoother transition to that down the road. I see that Mantra took quite the opposite stance and officially recommends JS styles. :) But of course, there is no right or wrong! Use what works for you, but always keep an eye out for what other people are coming up with. This landscape is constantly evolving.

PS: I'll update the example app to use BEM when I get some free time.

@dtwist
Copy link
Author

dtwist commented Mar 24, 2016

And certainly not all features will even use a lib or server folder, but the option is there. For example, I might have a /features/Billing/server folder to hold private Stripe-related stuff.

Hope that clears things up!

Generally, the idea of features makes perfect sense. But I'm still not understanding why I'd want (potentially sensitive) server-related code to reside in a subdirectory of the client folder. That means it will get incorporated into the client code, right? Even if it is dormant, it'll still be compiled in, no?

I've also moved away from JS styles to using BEM.

Yeah, that was the first thing to go as I started playing with the example branch. 😃 Inline styles in javascript are a fabulous idea if I want my designers' heads to explode before they hand in resignation letters!

Otherwise, my deviations from your direction so far are slight. I was already intending to use Astronomy, so was pleased to see that already in there; React and redux were on my hit-list too, so very nice. I decoupled the Redux Provider component from the layout, so I can keep alternate layouts DRY. I'm on the fence between react-komposer and react-mixin; And I'm toying with the idea of trialing react-router in place of flow-router.

@ffxsam
Copy link
Owner

ffxsam commented Mar 24, 2016

But I'm still not understanding why I'd want (potentially sensitive) server-related code to reside in a subdirectory of the client folder

It wouldn't. The structure goes like this:

features
└── Billing
    ├── client
    ├── lib
    └── server

And I'm toying with the idea of trialing react-router in place of flow-router.

Me too, actually. Since you're not supposed to have reactive data or serious business logic in Flow Router, I'm not seeing the advantage of it anymore over React Router, which is far less verbose. But I'm still mulling over the idea of moving.

@dtwist
Copy link
Author

dtwist commented Mar 24, 2016

Thanks for the quick responses!

It wouldn't. The structure goes like this:

Oh geez, I was misreading that structure in my tree view... heh. Even so, I thought any folder other than imports gets included in the build. If it's at the root, then its contents will get included in both client and server...

From the meteor guide (1.3):

Meteor will eagerly load any files outside of imports/ in the application

No?

@ffxsam
Copy link
Owner

ffxsam commented Mar 24, 2016

Correct. But.. quoting myself way above:

I feel [the imports folder is] a bridge as people who are used to the "globals everywhere" model of 1.2 move into 1.3 and get used to import/export. I have a feeling MDG will eventually make everything 100% explicit imports. Plus, my folder hierarchies are deep enough as it is, I didn't want to introduce yet another level!

Just personal preference.

@dtwist
Copy link
Author

dtwist commented Mar 24, 2016

Oh, I forgot: On the router, one other change, which I cribbed from @tomRedox's simpleCRM, and really like:

  // Set group for authenticated users
  const authenticatedRoutes = FlowRouter.group({
    name: 'authenticated',
    triggersEnter: [function(context, redirect) {
      // Redirect unauthed users to login page
      if (!Meteor.loggingIn() && !Meteor.userId()) {
        console.log('not authorized');
        redirect('/login');
      }
    }]
  });

  // --- PUBLIC ROUTES ---
  FlowRouter.route('/register', {
    name: 'users.new',
    action() {
      mount(MainLayoutCtx, {
        content: () => (<NewUser />)
      });
    }
  });

  FlowRouter.route('/login', {
    name: 'users.login',
    action() {
      mount(MainLayoutCtx, {
        content: () => (<Login />)
      });
    }
  });

  // --- AUTHENTICATED ROUTES ---
  authenticatedRoutes.route('/logout', {
    name: 'users.logout',
    action() {
      Meteor.logout();
      FlowRouter.go('/');
    }
  });

  authenticatedRoutes.route('/', {
    name: 'items.list',
    action() {
      mount(MainLayoutCtx, {
        content: () => (<EntryList />)
      });
    }
  });

...

I haven't looked closely enough at react-router to know how to implement the same so elegantly.

@dtwist
Copy link
Author

dtwist commented Mar 24, 2016

Correct. But.. quoting myself way above:

I feel [the imports folder is] a bridge as people who are used to the "globals everywhere" model of 1.2 move into 1.3 and get used to import/export. I have a feeling MDG will eventually make everything 100% explicit imports. Plus, my folder hierarchies are deep enough as it is, I didn't want to introduce yet another level!

Just personal preference.

Not to beat a dead horse, I just want to understand. In the here-and-now, this method means those files in the server directory will get incorporated on client and server, which seems wasteful of memory and bandwidth, and potentially a security issue. Or... 💡 Does Meteor exclude files from a "server" directory at any place in the hierarchy?

@ffxsam
Copy link
Owner

ffxsam commented Mar 24, 2016

Yeah, I like how React Router is just a handful of nested React components. Much shorter. But I don't know what the equivalent of FlowRouter.go() is in React Router. I use that quite a bit.

    triggersEnter: [function(context, redirect) {
      // Redirect unauthed users to login page
      if (!Meteor.loggingIn() && !Meteor.userId()) {
        console.log('not authorized');
        redirect('/login');
      }
    }]

I've seen many people recommend against this pattern, of handling authentication in Flow Router. I used to do this too. Instead, it's recommended that you handle auth and redirection in your React components.

Not to beat a dead horse, I just want to understand. In the here-and-now, this method means those files in the server directory will get incorporated on client and server, which seems wasteful of memory and bandwidth, and potentially a security issue.

imports has nothing to do with the separation of client and server. Anything inside imports is simply not globalized, that's all. But by not using imports, that doesn't really mean that code inside the server folder is bleeding out into client. So there's no concern of security. I'm just saving myself potential hassle of refactoring everything if/when MDG later decides everything will be explicit loading, and the imports folder will become obsolete.

@ffxsam
Copy link
Owner

ffxsam commented Mar 24, 2016

Now if you're just talking about the build tool and efficiency.. maybe that's something to consider. I don't know. I'm gonna keep beating the drum for the removal of lazy loading.. hopefully MDG will consider it. :) I'm not really a fan, but there's no way they could just remove it in 1.3. All the projects that rely on just using magical globals would break, and it would be disaster.

@dtwist
Copy link
Author

dtwist commented Mar 25, 2016

So, I did indeed have a fundamental misunderstanding of Meteor's build logic. I just tried including a simple console.log in a feature/x/server directory, and sure enough that is not built into the client. So any server directory, anywhere in the file hierarchy, is omitted from the client build. (And likewise for client dirs. omitted from server builds) That's excellent, and now the modular approach makes so much more sense, and aligns with my existing practices outside of Meteor. I kept getting tripped up thinking I had no choice but to use the root-level server directory if I wanted to isolate code. I'm in complete agreement with your approach then—lay down a structure now that "lives with" the magical globals, but code with explicit imports as preferred and in anticipation of the day that's the requirement. I don't see a major issue with the build efficiency at this point, but will keep that thought in the back of my mind.

@dtwist
Copy link
Author

dtwist commented Mar 25, 2016

Yeah, I like how React Router is just a handful of nested React components. Much shorter. But I don't know what the equivalent of FlowRouter.go() is in React Router. I use that quite a bit.

From the react-router tutorial docs:

// Repos.js
import { browserHistory } from 'react-router'

// ...
  handleSubmit(event) {
    // ...
    const path = `/repos/${userName}/${repo}`
    browserHistory.push(path)
  },
// ...

or...

export default React.createClass({

  // ask for `router` from context
  contextTypes: {
    router: React.PropTypes.object
  },

  // ...

  handleSubmit(event) {
    // ...
    this.context.router.push(path)
  },

  // ..
})

I've seen many people recommend against this pattern, of handling authentication in Flow Router. I used to do this too. Instead, it's recommended that you handle auth and redirection in your React components.

Huh, OK, thinking about it now, that starts to make sense. It's a paradigm-shift from my work with other frameworks, but it does rather make sense in React. (No need to worry about round-trips to a server for routing, the view-controller approach with React, etc.) My instinct then is to handle auth in the layout component (where we're also inserting the Redux Provider) and permissions in subsequent modules/features. This makes even more sense with react-router's nested routes, helping to keep permissions logic DRY.

@dtwist
Copy link
Author

dtwist commented Mar 25, 2016

Quoting myself:

My instinct then is to handle auth in the layout component (where we're also inserting the Redux Provider)

On second thought, that works in flow-router, where the layout decorates the route's component, but not in react-router, where (I guess) auth would have to be handled further down the hierarchy, along with permissions. 😞 Must go read and ponder more…

@ffxsam
Copy link
Owner

ffxsam commented Mar 26, 2016

Good luck. :)

@ffxsam ffxsam closed this as completed Mar 26, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants