Discuss about directory structure #5

Closed
ByJC opened this Issue Feb 3, 2016 · 64 comments

Comments

Projects
None yet
@ByJC

ByJC commented Feb 3, 2016

Hi @mgechev !
I just read your style guide, it's awesome ! I was wondering about directory structure.

you create a folder for each feature you have as following:

├── app
│   ├── about
│   │   └── components
│   │       ├── about.e2e.ts
│   │       ├── about.ts
│   │       └── about.spec.ts

You mention that if your folder grows to contains more than 7 files you create subfolders (components or services). it feels like it's grouped by type and not based on feature

I just tought to simply flatten the entire folder this way :

├── app
│   ├── about
│   │   └── about.e2e.ts
│   │   └── about.component.ts
│   │   └── about.service.ts
│   │   └──about.html (if you require the template)
│   │   └── about.css
│   │   └── about.spec.ts

In the case you have more than 10 files and the folder became difficult to read, I would suggest to create folders by sub-features

├── app
│   ├── todo
│   │   └── todo.e2e.ts
│   │   └── todo.component.ts
│   │   └── todo.service.ts
│   │   └── todo.html
│   │   ├── todoitem
│   │   │   └── todoitem.e2e.ts
│   │   │   └── todoitem.component.ts
│   │   │   └── todoitem.html
│   │   ├── todolist
│   │   │   └──todolist.e2e.ts
│   │   │   └── todolist.component.ts
│   │   │   └── todolist.html

What do you think about it ? I never worked on large-scale angular 2 application, only angular 1, so maybe I missed something.

@mgechev

This comment has been minimized.

Show comment
Hide comment
@mgechev

mgechev Feb 4, 2016

Owner

Another problem with the nested structure that we currently have is that when your component depends on a service in order to require it you need to type:

import {TodoStore} from '../services/todo_store';

Instead of simply:

import {TodoStore} from './todo_store.service';

To me the second option seems more reasonable, so I think your suggestion makes sense.

Owner

mgechev commented Feb 4, 2016

Another problem with the nested structure that we currently have is that when your component depends on a service in order to require it you need to type:

import {TodoStore} from '../services/todo_store';

Instead of simply:

import {TodoStore} from './todo_store.service';

To me the second option seems more reasonable, so I think your suggestion makes sense.

@ByJC

This comment has been minimized.

Show comment
Hide comment
@ByJC

ByJC Feb 4, 2016

@mgechev I have another questions about large-scale application in angular2.
you create for each feature a folder at the root of the app. But what about if you have 30 or more folders ? is it still easily readable ?
Have you already met the case and how do you manage your folder ?

ByJC commented Feb 4, 2016

@mgechev I have another questions about large-scale application in angular2.
you create for each feature a folder at the root of the app. But what about if you have 30 or more folders ? is it still easily readable ?
Have you already met the case and how do you manage your folder ?

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 4, 2016

What is the purpose of single "components" folder in each "feature" folder? Each component should have own folder, to keep ts, css and html files. I think it's obvious.

e-oz commented Feb 4, 2016

What is the purpose of single "components" folder in each "feature" folder? Each component should have own folder, to keep ts, css and html files. I think it's obvious.

@deeleman

This comment has been minimized.

Show comment
Hide comment
@deeleman

deeleman Feb 4, 2016

Contributor

But what about if you have 30 or more folders

I have this same concern too. Specially when it comes to nested components which are tightly coupled to its parent component so nesting folders makes more sense in order to favor encapsulation over reusability.

A calendar component, wrapping week, day, appointment and many more sub-components (which do not have much sense outside the scope of the calendar container component itself) might be a good example of this case-scenario.

Contributor

deeleman commented Feb 4, 2016

But what about if you have 30 or more folders

I have this same concern too. Specially when it comes to nested components which are tightly coupled to its parent component so nesting folders makes more sense in order to favor encapsulation over reusability.

A calendar component, wrapping week, day, appointment and many more sub-components (which do not have much sense outside the scope of the calendar container component itself) might be a good example of this case-scenario.

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 4, 2016

Collaborator

@ByJC The folder structure is by-feature not by-component. That's part of the reasoning behind having components, services, pipes, models subfolders.

For example:

.
├── app
│   ├── todo
│   │   ├── components
│   │   │   ├── todo.html
│   │   │   ├── todo.ts
│   │   │   ├── todoitem.ts
│   │   │   ├── todoitem.html
│   │   │   ├── todoitem.e2e.ts
│   │   │   ├── todolist.ts
│   │   │   ├── todolist.htm
│   │   │   ├── todolist.e2e.tsl
│   │   │   └── todoitem.ts
│   │   ├── models
│   │   ├── pipes
│   │   └── services
│   │       └── todo.ts  
│   ├── some_other_feature
│   │   ├── components
│   │   ├── models
│   │   ├── pipes
│   │   └── models
│   └──index.html  
└── package.json

The flattened structure can get really messy when you have a feature that is made up of a lot of components and models. Plus, the flattened structure requires a suffix for each type.

Flattened or not, it doesn't really matter. Organizing by-feature is the important part because it makes it easy to extract that code to be reused or versioned separately.


Modular API structure using the facade pattern

Whatever the structure, it doesn't matter if a facade is provided to hide the messy internals.

.
├── app
│   ├── todo
│   │   ├── components
│   │   │   ├── todo.html
│   │   │   ├── todo.ts
│   │   │   ├── todoitem.ts
│   │   │   ├── todoitem.html
│   │   │   ├── todoitem.e2e.ts
│   │   │   ├── todolist.ts
│   │   │   ├── todolist.htm
│   │   │   ├── todolist.e2e.tsl
│   │   │   └── todoitem.ts
│   │   ├── models
│   │   ├── pipes
│   │   ├── services
│   │   │   └── todo.ts  
│   │   └── todo.js <- module facade

The facade is a mapping of imports to exports to provide a clean external API.

app/todo/todo.ts

import { TodoCmp as TODO } from './components/todo';
import { TodoItemCmp } from './components/todoitem';
import { TodoListCmp } from './components/todolist';
import { TodoService } from './services/todo';

export default TODO;

export const TODO_COMPONENTS = [
  TodoCmp,
  TodoItemCmp,
  TodoListCmp,
];

export const TODO_SERVICES = [
  TodoService
];

The facade makes managing imports from external dependencies really nice... With facades there's no need to waste cognitive overhead memorizing the folder structure of every dependency that is imported. Rxjs in it's current form is a perfect example of a lib that has grown to the size that it could really use a facade.

import 'app/todo/todo';

  • can be used to import the whole todo feature into any other part of the application.

import { TODO_COMPONENTS, TODO_SERVICES } from 'app/todo/todo';

  • can be used if it's desirable to use the internals piecemeal
  • for instance, adding the services during bootstrap

Note: It would be really awesome if Angular2 recognized a certain script as the default for a folder (ex index.js). That way the import path could be shortened from app/todo/todo -> app/todo.

To see this in action, I use it extensively in ng2-resume to compose higher order components from sub-components and sub-sub-components.

For an example of reuse, ng2-resume was mostly developed as a feature of evanplaice.com before it was extracted into it's own repo. After it was extracted all I had to do was install it via JSPM and fix a single import.

The same approach was used when I created the ng2-markdown-component.

Note: If I forget to come back and update this later, ng2-markdown-component will be renamed to just ng2-markdown. Naming is hard ¯_(ツ)/¯._

Collaborator

evanplaice commented Feb 4, 2016

@ByJC The folder structure is by-feature not by-component. That's part of the reasoning behind having components, services, pipes, models subfolders.

For example:

.
├── app
│   ├── todo
│   │   ├── components
│   │   │   ├── todo.html
│   │   │   ├── todo.ts
│   │   │   ├── todoitem.ts
│   │   │   ├── todoitem.html
│   │   │   ├── todoitem.e2e.ts
│   │   │   ├── todolist.ts
│   │   │   ├── todolist.htm
│   │   │   ├── todolist.e2e.tsl
│   │   │   └── todoitem.ts
│   │   ├── models
│   │   ├── pipes
│   │   └── services
│   │       └── todo.ts  
│   ├── some_other_feature
│   │   ├── components
│   │   ├── models
│   │   ├── pipes
│   │   └── models
│   └──index.html  
└── package.json

The flattened structure can get really messy when you have a feature that is made up of a lot of components and models. Plus, the flattened structure requires a suffix for each type.

Flattened or not, it doesn't really matter. Organizing by-feature is the important part because it makes it easy to extract that code to be reused or versioned separately.


Modular API structure using the facade pattern

Whatever the structure, it doesn't matter if a facade is provided to hide the messy internals.

.
├── app
│   ├── todo
│   │   ├── components
│   │   │   ├── todo.html
│   │   │   ├── todo.ts
│   │   │   ├── todoitem.ts
│   │   │   ├── todoitem.html
│   │   │   ├── todoitem.e2e.ts
│   │   │   ├── todolist.ts
│   │   │   ├── todolist.htm
│   │   │   ├── todolist.e2e.tsl
│   │   │   └── todoitem.ts
│   │   ├── models
│   │   ├── pipes
│   │   ├── services
│   │   │   └── todo.ts  
│   │   └── todo.js <- module facade

The facade is a mapping of imports to exports to provide a clean external API.

app/todo/todo.ts

import { TodoCmp as TODO } from './components/todo';
import { TodoItemCmp } from './components/todoitem';
import { TodoListCmp } from './components/todolist';
import { TodoService } from './services/todo';

export default TODO;

export const TODO_COMPONENTS = [
  TodoCmp,
  TodoItemCmp,
  TodoListCmp,
];

export const TODO_SERVICES = [
  TodoService
];

The facade makes managing imports from external dependencies really nice... With facades there's no need to waste cognitive overhead memorizing the folder structure of every dependency that is imported. Rxjs in it's current form is a perfect example of a lib that has grown to the size that it could really use a facade.

import 'app/todo/todo';

  • can be used to import the whole todo feature into any other part of the application.

import { TODO_COMPONENTS, TODO_SERVICES } from 'app/todo/todo';

  • can be used if it's desirable to use the internals piecemeal
  • for instance, adding the services during bootstrap

Note: It would be really awesome if Angular2 recognized a certain script as the default for a folder (ex index.js). That way the import path could be shortened from app/todo/todo -> app/todo.

To see this in action, I use it extensively in ng2-resume to compose higher order components from sub-components and sub-sub-components.

For an example of reuse, ng2-resume was mostly developed as a feature of evanplaice.com before it was extracted into it's own repo. After it was extracted all I had to do was install it via JSPM and fix a single import.

The same approach was used when I created the ng2-markdown-component.

Note: If I forget to come back and update this later, ng2-markdown-component will be renamed to just ng2-markdown. Naming is hard ¯_(ツ)/¯._

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 4, 2016

Multiple components in one folder is a pile. Even if they all are coupled to one feature.

I personally can't read this:

│   │   ├── components
│   │   │   ├── todo.html
│   │   │   ├── todo.ts
│   │   │   ├── todoitem.ts
│   │   │   ├── todoitem.html
│   │   │   ├── todoitem.e2e.ts
│   │   │   ├── todolist.ts
│   │   │   ├── todolist.htm
│   │   │   ├── todolist.e2e.tsl
│   │   │   └── todoitem.ts

Just some list of files, all with one prefix.

  1. When you need to prefix multiple things with same prefix, it's a good sign to encapsulate them inside folder/entity/table with name of this prefix.
  2. Each component is a capsule, multiple templates in one folder means we have guts of multiple components in one box, mixed (filesystem will not always sort them logically). Better to use folders:
app/
---/components
---/---/home
---/---/---/home.html
---/---/---/home.ts
---/---/login
---/---/---/login.html
---/---/---/login.ts
---/---/contract
---/---/---/contract-item
---/---/---/---/contract-item.html
---/---/---/---/contract-item.ts
---/---/---/contract.html
---/---/---/contract.ts
---/---/select-box
---/---/---/select-box.html
---/---/---/select-box.ts
---/---/---/select-box.css

In this example, most of components should be in root of the "components" folder, and they all should be a folders (with template, component class, and css if needed). It will encourage people to create really encapsulated components, not coupled to other components tightly, to be able reuse them as much as possible in other apps, to be able remove them from this structure seamlessly and replace by npm modules.

Some components, of course, are too specific to be reused, but they should be in minority. And they deserve own folders, to keep structure clean and.. structured, predictable. I have 2 apps built this way and this structure works great (in second app I have ~70 components and 5 nested).

e-oz commented Feb 4, 2016

Multiple components in one folder is a pile. Even if they all are coupled to one feature.

I personally can't read this:

│   │   ├── components
│   │   │   ├── todo.html
│   │   │   ├── todo.ts
│   │   │   ├── todoitem.ts
│   │   │   ├── todoitem.html
│   │   │   ├── todoitem.e2e.ts
│   │   │   ├── todolist.ts
│   │   │   ├── todolist.htm
│   │   │   ├── todolist.e2e.tsl
│   │   │   └── todoitem.ts

Just some list of files, all with one prefix.

  1. When you need to prefix multiple things with same prefix, it's a good sign to encapsulate them inside folder/entity/table with name of this prefix.
  2. Each component is a capsule, multiple templates in one folder means we have guts of multiple components in one box, mixed (filesystem will not always sort them logically). Better to use folders:
app/
---/components
---/---/home
---/---/---/home.html
---/---/---/home.ts
---/---/login
---/---/---/login.html
---/---/---/login.ts
---/---/contract
---/---/---/contract-item
---/---/---/---/contract-item.html
---/---/---/---/contract-item.ts
---/---/---/contract.html
---/---/---/contract.ts
---/---/select-box
---/---/---/select-box.html
---/---/---/select-box.ts
---/---/---/select-box.css

In this example, most of components should be in root of the "components" folder, and they all should be a folders (with template, component class, and css if needed). It will encourage people to create really encapsulated components, not coupled to other components tightly, to be able reuse them as much as possible in other apps, to be able remove them from this structure seamlessly and replace by npm modules.

Some components, of course, are too specific to be reused, but they should be in minority. And they deserve own folders, to keep structure clean and.. structured, predictable. I have 2 apps built this way and this structure works great (in second app I have ~70 components and 5 nested).

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 4, 2016

Collaborator

@e-oz by-feature is the convention recommended in this styleguide due to the fact that grouping by-type destroys reusability.

What happens if/when you want to extract the Todo feature as an module to be versioned independently and reused in other applications?

There's a reason by-feature was chosen as the convention. It decouples the structure of the code from the structure of the application. With this approach, each feature can be viewed as it's own self-contained application. If you choose to extract the component into its own repo, it can literally be used as a self contained application.

Using the by-feature approach, if a feature becomes to unwieldy another level of nesting can be included to specify sub features. If you see it as a self contained application, it's structure will look the same as the application it's contained in.

Some might even say it's....

componentception

Collaborator

evanplaice commented Feb 4, 2016

@e-oz by-feature is the convention recommended in this styleguide due to the fact that grouping by-type destroys reusability.

What happens if/when you want to extract the Todo feature as an module to be versioned independently and reused in other applications?

There's a reason by-feature was chosen as the convention. It decouples the structure of the code from the structure of the application. With this approach, each feature can be viewed as it's own self-contained application. If you choose to extract the component into its own repo, it can literally be used as a self contained application.

Using the by-feature approach, if a feature becomes to unwieldy another level of nesting can be included to specify sub features. If you see it as a self contained application, it's structure will look the same as the application it's contained in.

Some might even say it's....

componentception

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 4, 2016

@evanplaice nothing criminal will happen, in my proposal "todo" is a component and it will be happily reused as npm module. Each feature should be a component.

If we are using words "chosen" as something what can't be changed, then there is no reason to discuss it at all and we can just agree to disagree :)

e-oz commented Feb 4, 2016

@evanplaice nothing criminal will happen, in my proposal "todo" is a component and it will be happily reused as npm module. Each feature should be a component.

If we are using words "chosen" as something what can't be changed, then there is no reason to discuss it at all and we can just agree to disagree :)

@ByJC

This comment has been minimized.

Show comment
Hide comment
@ByJC

ByJC Feb 4, 2016

@evanplaice Why can't you extract the todo feature as you said from the explanation and structure of @e-oz ?

ByJC commented Feb 4, 2016

@evanplaice Why can't you extract the todo feature as you said from the explanation and structure of @e-oz ?

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 4, 2016

Collaborator

@e-oz And when you go to extract it you'll have to rip out and combine the components, services, and models; create a structure identical to what the by-feature approach already provides; and remap all of the import statements to avoid breaking the application.

If you were already using that structure, it would be a simple folder copy. Everything is already wired up with relative links.

Collaborator

evanplaice commented Feb 4, 2016

@e-oz And when you go to extract it you'll have to rip out and combine the components, services, and models; create a structure identical to what the by-feature approach already provides; and remap all of the import statements to avoid breaking the application.

If you were already using that structure, it would be a simple folder copy. Everything is already wired up with relative links.

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 4, 2016

@evanplaice you are mixing 2 kinds of dependencies.

  1. Components can have dependencies of services - it's out of this discussion, because Services are much more encapsulated and should know nothing about components, templates and even your app. Services are black boxes and DI gives us this feature.
  2. Also components can be dependent on other components, they reuse them inside of templates. And in this case, if we will keep some component, let's say "select-box" inside of feature "header", then each other component will be wired with header just to reuse it's sub-component. At some point we may think "select-box" is unique to "header" feature, but later we will realize we can reuse it - so we should try to encapsulated it from the beginning, by not moving to parent's (feature) folder, but making it independent. As I said, it's not 100% rule, but more is better here.

e-oz commented Feb 4, 2016

@evanplaice you are mixing 2 kinds of dependencies.

  1. Components can have dependencies of services - it's out of this discussion, because Services are much more encapsulated and should know nothing about components, templates and even your app. Services are black boxes and DI gives us this feature.
  2. Also components can be dependent on other components, they reuse them inside of templates. And in this case, if we will keep some component, let's say "select-box" inside of feature "header", then each other component will be wired with header just to reuse it's sub-component. At some point we may think "select-box" is unique to "header" feature, but later we will realize we can reuse it - so we should try to encapsulated it from the beginning, by not moving to parent's (feature) folder, but making it independent. As I said, it's not 100% rule, but more is better here.
@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 4, 2016

Collaborator

@e-oz That's the point. They're not 'mixed' as much as 'grouped' logically based on the functionality they provide.

  1. That doesn't make any sense. If you reuse a feature across many applications, do you re-implement the service for every application you import it into? A service manages the state and handles requests to external resources for the components. If you make a service single-purpose to act as the data layer of feature, it can be packaged for reuse.
  2. The select-box will likely be reused in multiple places throughout the application so it'd put it under 'shared'. HeaderCmp will likely be a subcomponent of AppCmp so I'd put it there.
Collaborator

evanplaice commented Feb 4, 2016

@e-oz That's the point. They're not 'mixed' as much as 'grouped' logically based on the functionality they provide.

  1. That doesn't make any sense. If you reuse a feature across many applications, do you re-implement the service for every application you import it into? A service manages the state and handles requests to external resources for the components. If you make a service single-purpose to act as the data layer of feature, it can be packaged for reuse.
  2. The select-box will likely be reused in multiple places throughout the application so it'd put it under 'shared'. HeaderCmp will likely be a subcomponent of AppCmp so I'd put it there.
@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 4, 2016

@evanplaice

  1. It actually means Service is much more reusable and can be moved to node_modules even faster.
  2. Why you think so? Because of name 'select-box'? It's wrong approach. We should try to create reusable component first, not coupled to some feature. Otherwise we will end with huge amount of copy-pasted components with slight changes for each feature. In your example "shared" is last point of technical debt for me, because all components should be able to be "shared" from the beginning, it's why they are called "components". And there are non-0 chance you'll want to reuse even your HeaderCmp in other apps.

e-oz commented Feb 4, 2016

@evanplaice

  1. It actually means Service is much more reusable and can be moved to node_modules even faster.
  2. Why you think so? Because of name 'select-box'? It's wrong approach. We should try to create reusable component first, not coupled to some feature. Otherwise we will end with huge amount of copy-pasted components with slight changes for each feature. In your example "shared" is last point of technical debt for me, because all components should be able to be "shared" from the beginning, it's why they are called "components". And there are non-0 chance you'll want to reuse even your HeaderCmp in other apps.
@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 4, 2016

Collaborator

@e-oz

  1. So you'd package the service as it's own module, then package each component and it's subcomponents as their own modules, same for pipes, also don't forget about models.
  2. A feature is reusable by default. There is no copy-pasted code, everything is logically grouped. And yes there's absolutely zero chance I want to reuse HeaderCmp because it contains router links that are specific to that application.

Enough with the theoretical axe grinding. Show proof (in the form of actual code) that your approach provides a clearer path to reusability. In the form of a reusable component and 2 or more applications where is imported as a dependency. Here's mine.

Component: ng2-resume
Application1: evanplaice.com
Application2: ng2-resume-demo

Here are links where the feature can actually be seen in action:

I don't expect proof now but feel free to come back and ping me when you're ready so we can do a comparison.

Collaborator

evanplaice commented Feb 4, 2016

@e-oz

  1. So you'd package the service as it's own module, then package each component and it's subcomponents as their own modules, same for pipes, also don't forget about models.
  2. A feature is reusable by default. There is no copy-pasted code, everything is logically grouped. And yes there's absolutely zero chance I want to reuse HeaderCmp because it contains router links that are specific to that application.

Enough with the theoretical axe grinding. Show proof (in the form of actual code) that your approach provides a clearer path to reusability. In the form of a reusable component and 2 or more applications where is imported as a dependency. Here's mine.

Component: ng2-resume
Application1: evanplaice.com
Application2: ng2-resume-demo

Here are links where the feature can actually be seen in action:

I don't expect proof now but feel free to come back and ping me when you're ready so we can do a comparison.

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 4, 2016

You forgot "please", @evanplaice . I'm not going to write code for you and spend my time, sorry. I have experience with my approach, as I said, and I don't have to prove anything to you (only difference is I'm not trying to call you a lier), just let's keep different opinions on this subject, it's ok to have different opinions. Bye.

e-oz commented Feb 4, 2016

You forgot "please", @evanplaice . I'm not going to write code for you and spend my time, sorry. I have experience with my approach, as I said, and I don't have to prove anything to you (only difference is I'm not trying to call you a lier), just let's keep different opinions on this subject, it's ok to have different opinions. Bye.

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 4, 2016

@evanplaice your updates to posts are almost not visible to me, I'm only getting notifications on first submission - it's how github works, so please consider to write separate messages, not updates to existing.

Thanks for "feel free", but I'm not going to prove you anything, really :) My proposal is 100% understandable for other readers.
If you think I don't have practice with it, here is screenshot of one of apps structure:
screenshot 2016-02-04 18 19 23
Some components are in npm_modules.
It's all I can give you - I better spend some time to my family, rather than arguing in internets.
And don't call random people liars, it's kind of rude.

e-oz commented Feb 4, 2016

@evanplaice your updates to posts are almost not visible to me, I'm only getting notifications on first submission - it's how github works, so please consider to write separate messages, not updates to existing.

Thanks for "feel free", but I'm not going to prove you anything, really :) My proposal is 100% understandable for other readers.
If you think I don't have practice with it, here is screenshot of one of apps structure:
screenshot 2016-02-04 18 19 23
Some components are in npm_modules.
It's all I can give you - I better spend some time to my family, rather than arguing in internets.
And don't call random people liars, it's kind of rude.

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 4, 2016

Collaborator

@e-oz How do you take disagreement as the equivalent of calling you a liar. I'm not even claiming that your wrong. I just saw the commentary was stuck in a bad look and tried to break out of it by taking a more objective approach.

I would genuinely like to see 2 equivalent samples to compare, contrast, and check assumptions. Maybe there's room for improvement.

Anyway, thank you for sharing, I understand that not everybody has code immediately available to share on GH. It looks like you have a pretty good logical structure there. My only disappointment is that I can't dig deeper and see how it all fits together. I'm sorry to keep you from your family.

Collaborator

evanplaice commented Feb 4, 2016

@e-oz How do you take disagreement as the equivalent of calling you a liar. I'm not even claiming that your wrong. I just saw the commentary was stuck in a bad look and tried to break out of it by taking a more objective approach.

I would genuinely like to see 2 equivalent samples to compare, contrast, and check assumptions. Maybe there's room for improvement.

Anyway, thank you for sharing, I understand that not everybody has code immediately available to share on GH. It looks like you have a pretty good logical structure there. My only disappointment is that I can't dig deeper and see how it all fits together. I'm sorry to keep you from your family.

@mgechev

This comment has been minimized.

Show comment
Hide comment
@mgechev

mgechev Feb 5, 2016

Owner

I'd prefer to stick to the by-feature folder structure, because of the reasons mentioned above.

It is also a good question whether once grouped by feature the components should be also grouped by type. If we take this further and there are a lot of components for given feature, should we create another set of directories for the individual components?

For instance in the calendar example:

- schedule
   - calendar
      - components
        - calendar.html
        - calendar.ts
        - calendar.spec.ts
        - month.html
        - month.ts
        - month.spec.ts
        - day.html
        - day.ts
    - services
      - foo.ts
      - bar.ts

Whether we are going to use custom suffixes for the code units (i.e. calendar.component.ts and foo.service) depends completely on whether we'll group them by type once grouped by feature.

Although creating such a nested structure might be a bit annoying while navigating through your project it somehow looks more organized to me.

Owner

mgechev commented Feb 5, 2016

I'd prefer to stick to the by-feature folder structure, because of the reasons mentioned above.

It is also a good question whether once grouped by feature the components should be also grouped by type. If we take this further and there are a lot of components for given feature, should we create another set of directories for the individual components?

For instance in the calendar example:

- schedule
   - calendar
      - components
        - calendar.html
        - calendar.ts
        - calendar.spec.ts
        - month.html
        - month.ts
        - month.spec.ts
        - day.html
        - day.ts
    - services
      - foo.ts
      - bar.ts

Whether we are going to use custom suffixes for the code units (i.e. calendar.component.ts and foo.service) depends completely on whether we'll group them by type once grouped by feature.

Although creating such a nested structure might be a bit annoying while navigating through your project it somehow looks more organized to me.

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 5, 2016

All files of calendar without folders is a mess, and this mess will grow with count of components.
And nesting them inside parent component will encourage to make them coupled, not reusable. I've said my words, now I'll shut up :)

e-oz commented Feb 5, 2016

All files of calendar without folders is a mess, and this mess will grow with count of components.
And nesting them inside parent component will encourage to make them coupled, not reusable. I've said my words, now I'll shut up :)

@the-ult

This comment has been minimized.

Show comment
Hide comment
@the-ult

the-ult Feb 5, 2016

I'd like to seperate specific view/route component from the more commen components which are reusable and might end up in a seperate component repository/library, so we can load them with npm.
Besides the view component you might also have layout specific components like the navbar, side-bar, footer, header, etc.

I was wondering what the best place is to store layout specific components.

The way we are handeling it now is more like:

.
├── README.md
├── app
│   ├── assets
│   │   ├── img
│   │   │   └── smile.png
│   │   └── main.css
│   ├── components
│   │   └── list
│   │       ├── list.component.e2e.ts
│   │       ├── list.component.html
│   │       ├── list.component.ts
│   │       └── list.component.spec.ts
│   │       ├── list-item.component.e2e.ts
│   │       ├── list-item.component.html
│   │       ├── list-item.component.ts
│   │       └── list-item.component.spec.ts
│   ├── core
│   │   └── common
│   │   └── services
│   │       ├── invoice-service.ts
│   │   │  └── models
│   │   │   │  ├── invoice.model.ts
│   ├── layout
│   │   └── app
│   │       ├── app.css
│   │       ├── app.html
│   │       ├── app.ts
│   │       ├── app.e2e.ts
│   │       └── app.spec.ts
│   │   └── footer
│   │   └── navigation
│   │   └── sprite
│   ├── views (routes/states??)
│   │   └── home
│   │       ├── home.css
│   │       ├── home.html
│   │       ├── home.ts
│   │       ├── home.e2e.ts
│   │       └── home.spec.ts
│   │   └── invoice-detail
│   │   └── login
│   │   └── user-profile

This still doesn't feel right. But putting common components and layout and feature/view components together doesn't feel good either.

the-ult commented Feb 5, 2016

I'd like to seperate specific view/route component from the more commen components which are reusable and might end up in a seperate component repository/library, so we can load them with npm.
Besides the view component you might also have layout specific components like the navbar, side-bar, footer, header, etc.

I was wondering what the best place is to store layout specific components.

The way we are handeling it now is more like:

.
├── README.md
├── app
│   ├── assets
│   │   ├── img
│   │   │   └── smile.png
│   │   └── main.css
│   ├── components
│   │   └── list
│   │       ├── list.component.e2e.ts
│   │       ├── list.component.html
│   │       ├── list.component.ts
│   │       └── list.component.spec.ts
│   │       ├── list-item.component.e2e.ts
│   │       ├── list-item.component.html
│   │       ├── list-item.component.ts
│   │       └── list-item.component.spec.ts
│   ├── core
│   │   └── common
│   │   └── services
│   │       ├── invoice-service.ts
│   │   │  └── models
│   │   │   │  ├── invoice.model.ts
│   ├── layout
│   │   └── app
│   │       ├── app.css
│   │       ├── app.html
│   │       ├── app.ts
│   │       ├── app.e2e.ts
│   │       └── app.spec.ts
│   │   └── footer
│   │   └── navigation
│   │   └── sprite
│   ├── views (routes/states??)
│   │   └── home
│   │       ├── home.css
│   │       ├── home.html
│   │       ├── home.ts
│   │       ├── home.e2e.ts
│   │       └── home.spec.ts
│   │   └── invoice-detail
│   │   └── login
│   │   └── user-profile

This still doesn't feel right. But putting common components and layout and feature/view components together doesn't feel good either.

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 5, 2016

@the-ult any component is routable in ng2.

e-oz commented Feb 5, 2016

@the-ult any component is routable in ng2.

@the-ult

This comment has been minimized.

Show comment
Hide comment
@the-ult

the-ult Feb 5, 2016

@e-oz , you're right.. But that's how it work technically..
But in practice, you still have view/page components like home, user-profile, etc. And layout components like navigation, side-bar etc.
So while navigation your code it would be nice to find/group the different kind of components together.

the-ult commented Feb 5, 2016

@e-oz , you're right.. But that's how it work technically..
But in practice, you still have view/page components like home, user-profile, etc. And layout components like navigation, side-bar etc.
So while navigation your code it would be nice to find/group the different kind of components together.

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 5, 2016

@the-ult I disagree. User profile easily can be embedded component and also routable page. Your proposal will encourage people to create components, which are only usable as routable pages, when they could have chance to reuse them as real components.

e-oz commented Feb 5, 2016

@the-ult I disagree. User profile easily can be embedded component and also routable page. Your proposal will encourage people to create components, which are only usable as routable pages, when they could have chance to reuse them as real components.

@the-ult

This comment has been minimized.

Show comment
Hide comment
@the-ult

the-ult Feb 5, 2016

That's true.
And how about the navigation/side-bar etc components? It won't take long before we exceed the 7 components limit per tree..

@e-oz: I meant the suggestion/best-practice/advice

When a folder grows to contain more than 7 files, start to consider creating a folder for them.

the-ult commented Feb 5, 2016

That's true.
And how about the navigation/side-bar etc components? It won't take long before we exceed the 7 components limit per tree..

@e-oz: I meant the suggestion/best-practice/advice

When a folder grows to contain more than 7 files, start to consider creating a folder for them.

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 5, 2016

@the-ult sorry, maybe I missed that "7 items" limitation. I don't understand why not 8, 3 or 99, so don't see point in such limitation.

e-oz commented Feb 5, 2016

@the-ult sorry, maybe I missed that "7 items" limitation. I don't understand why not 8, 3 or 99, so don't see point in such limitation.

@deeleman

This comment has been minimized.

Show comment
Hide comment
@deeleman

deeleman Feb 6, 2016

Contributor

It is also a good question whether once grouped by feature the components should be also grouped by type. If we take this further and there are a lot of components for given feature, should we create another set of directories for the individual components?

I share that very same concern as well. Let's summarize some pros and cons of this and try to take a bird's view of the current stage of this discussion.

The approach pointed out in Minko's comment features two main pros (which are open to discussion though):

  1. Splitting components by folder within the feature domain tree makes it easier to remove the files belonging to a specific component when such component (or components) is ditched from that feature, also removing the (now unnecessary) child components in one go. We can safely remove the specific component folder instead of pinpointing the files inside the feature's unique components folder, which can lead to either remove files by mistake or leave unused files cluttering the project workspace.
  2. We can clearly assess the application components hierarchy just by taking a look into the feature branches and then the component folders tree within each one. Here the file directory itself sets the narrative of our application.

Regarding the latter, many people will say that the filenames themselves tell the whole story (which is correct for small-to-mid sized projects) and it just takes a quick overview of such filenames to clearly see which is the root component for each feature. Taking the TODO example for instance (HTML templates or spec files removed for brevity sake but try to imagine how this would look with all companion files in place - I have left the .component suffix in the examples until there is an agreement on that issue as well):

│   organizer-app
│   ├── todo-list
│   │   └── components
│   │       ├── list.component.ts
│   │       ├── list-item.component.ts
│   │       ├── list-item-options.component.ts
│   │       └── list-item-options-option.component.ts

However, what would happen if we bring a generic component from the outside world into the context of this feature and we do not want to leverage a top root shared folder for whatever reason. P.eg.

│   organizer-app
│   ├── todo-list
│   │   └── components
│   │       ├── list.component.ts
│   │       ├── list-item.component.ts
│   │       ├── list-item-options.component.ts
│   │       ├── list-item-options-option.component.ts
│   │       └── vendor-fancy-checkbox.component.ts // <-- Hello, I'm new to the party!

Can we tell where is this component being used just by looking at the folder contents? Obviously not. Not a big deal in our todo example, but a bit of a burden in bigger projects.

We must also assess the impact of the following:

Create folders named for the feature they represent. When a folder grows to contain more than 7 files, start to consider creating a folder for them. Your threshold may be different, so adjust as needed.

How would the directory tree get with a sub tree structure based on all the requirements pointed out above?

│   /organizer-app
│   ├── /todo-list
│   │   └── /components
│   │   │   ├── list.component.ts
│   │   │   └── /list-item
│   │   │          └── /components 
│   │   │                  ├── list-item.component.ts
│   │   │                  └── /list-item-options
│   │   │                       └── /components
│   │   │                       │      └── list-item-options.component.ts
│   │   │                       └── /directives
│   │   │                                └── vendor-fancy-checkbox.directive.ts
│   │   └── /services
│   │       └── list.service.ts

In the example above each sub-component not only features its own folder but its own components folder along with its own services, directives, pipes, etc where required...

There are some cons and additional concerns with this approach though (I came up with just two, but there're obviously more):

  • Smaller projects with just a couple of components become kind of bloated with folders.
  • Do we really need to store the directives or pipes that are used by a very specific component in its own folder? Shall we dump them in the same sub-feature/sub-components folder (in which case the components folder is fairly redundant and we can save a level) or store them in a directives or pipes subfolder along with the components folder (having import WhateverType from '../type/whatever.ts' instead of import WhateverType from './whatever.type.ts' blocks at the top, as someone pointed out already in this same thread).

The problem with this last concern is that we will wind up confusing sub-features with sub-components in the semantic context of our project.

This is a hot topic definitely.

Contributor

deeleman commented Feb 6, 2016

It is also a good question whether once grouped by feature the components should be also grouped by type. If we take this further and there are a lot of components for given feature, should we create another set of directories for the individual components?

I share that very same concern as well. Let's summarize some pros and cons of this and try to take a bird's view of the current stage of this discussion.

The approach pointed out in Minko's comment features two main pros (which are open to discussion though):

  1. Splitting components by folder within the feature domain tree makes it easier to remove the files belonging to a specific component when such component (or components) is ditched from that feature, also removing the (now unnecessary) child components in one go. We can safely remove the specific component folder instead of pinpointing the files inside the feature's unique components folder, which can lead to either remove files by mistake or leave unused files cluttering the project workspace.
  2. We can clearly assess the application components hierarchy just by taking a look into the feature branches and then the component folders tree within each one. Here the file directory itself sets the narrative of our application.

Regarding the latter, many people will say that the filenames themselves tell the whole story (which is correct for small-to-mid sized projects) and it just takes a quick overview of such filenames to clearly see which is the root component for each feature. Taking the TODO example for instance (HTML templates or spec files removed for brevity sake but try to imagine how this would look with all companion files in place - I have left the .component suffix in the examples until there is an agreement on that issue as well):

│   organizer-app
│   ├── todo-list
│   │   └── components
│   │       ├── list.component.ts
│   │       ├── list-item.component.ts
│   │       ├── list-item-options.component.ts
│   │       └── list-item-options-option.component.ts

However, what would happen if we bring a generic component from the outside world into the context of this feature and we do not want to leverage a top root shared folder for whatever reason. P.eg.

│   organizer-app
│   ├── todo-list
│   │   └── components
│   │       ├── list.component.ts
│   │       ├── list-item.component.ts
│   │       ├── list-item-options.component.ts
│   │       ├── list-item-options-option.component.ts
│   │       └── vendor-fancy-checkbox.component.ts // <-- Hello, I'm new to the party!

Can we tell where is this component being used just by looking at the folder contents? Obviously not. Not a big deal in our todo example, but a bit of a burden in bigger projects.

We must also assess the impact of the following:

Create folders named for the feature they represent. When a folder grows to contain more than 7 files, start to consider creating a folder for them. Your threshold may be different, so adjust as needed.

How would the directory tree get with a sub tree structure based on all the requirements pointed out above?

│   /organizer-app
│   ├── /todo-list
│   │   └── /components
│   │   │   ├── list.component.ts
│   │   │   └── /list-item
│   │   │          └── /components 
│   │   │                  ├── list-item.component.ts
│   │   │                  └── /list-item-options
│   │   │                       └── /components
│   │   │                       │      └── list-item-options.component.ts
│   │   │                       └── /directives
│   │   │                                └── vendor-fancy-checkbox.directive.ts
│   │   └── /services
│   │       └── list.service.ts

In the example above each sub-component not only features its own folder but its own components folder along with its own services, directives, pipes, etc where required...

There are some cons and additional concerns with this approach though (I came up with just two, but there're obviously more):

  • Smaller projects with just a couple of components become kind of bloated with folders.
  • Do we really need to store the directives or pipes that are used by a very specific component in its own folder? Shall we dump them in the same sub-feature/sub-components folder (in which case the components folder is fairly redundant and we can save a level) or store them in a directives or pipes subfolder along with the components folder (having import WhateverType from '../type/whatever.ts' instead of import WhateverType from './whatever.type.ts' blocks at the top, as someone pointed out already in this same thread).

The problem with this last concern is that we will wind up confusing sub-features with sub-components in the semantic context of our project.

This is a hot topic definitely.

@c-ice

This comment has been minimized.

Show comment
Hide comment
@c-ice

c-ice Feb 10, 2016

hello

  1. What about rename folder "shared" to "common"? Common sound more like some "module" or something that is like "core" part of application.
  2. About previous structure discussion I think that right structure is combination between all options that was discussed. Definitely with facade pattern and naming with type. Something like this:
    (its flatty because project will be really big?)
├── src
│   ├── todo
│   │   ├── todo.js <- module facade
│   │   ├── todo.view.html ---> [view] for pages
│   │   ├── todo.component.ts
│   │   ├── todo.service.ts
│   │   ├── todo.e2e.ts
│   │   ├── todoItem
│   │   │   ├── todoitem.component.ts
│   │   │   ├── todoitem.html ----> no view its not page
│   │   │   └── todoitem.e2e.ts
│   │   ├── todoList
│   │   │   ├── todolist.component.ts
│   │   │   ├── todolist.html
│   │   │   └── todolist.e2e.ts
│   │   X── models ----> * because models are shared
│   │   ├── pipes
│   │   └── ...by-type... ---> ** group by type when it will be messy

* it'll better to collect them at one place if you are using ERM, relationship-database. Because its very probably that you will need the model at a lot of different places. (but not so good for re-use)
** e.g. one whatever.service.ts is OK to leave at root but two or more services rather group to one folder services

And at the end some structure is better for apps(small/mid/large) another better for libs maybe.

c-ice commented Feb 10, 2016

hello

  1. What about rename folder "shared" to "common"? Common sound more like some "module" or something that is like "core" part of application.
  2. About previous structure discussion I think that right structure is combination between all options that was discussed. Definitely with facade pattern and naming with type. Something like this:
    (its flatty because project will be really big?)
├── src
│   ├── todo
│   │   ├── todo.js <- module facade
│   │   ├── todo.view.html ---> [view] for pages
│   │   ├── todo.component.ts
│   │   ├── todo.service.ts
│   │   ├── todo.e2e.ts
│   │   ├── todoItem
│   │   │   ├── todoitem.component.ts
│   │   │   ├── todoitem.html ----> no view its not page
│   │   │   └── todoitem.e2e.ts
│   │   ├── todoList
│   │   │   ├── todolist.component.ts
│   │   │   ├── todolist.html
│   │   │   └── todolist.e2e.ts
│   │   X── models ----> * because models are shared
│   │   ├── pipes
│   │   └── ...by-type... ---> ** group by type when it will be messy

* it'll better to collect them at one place if you are using ERM, relationship-database. Because its very probably that you will need the model at a lot of different places. (but not so good for re-use)
** e.g. one whatever.service.ts is OK to leave at root but two or more services rather group to one folder services

And at the end some structure is better for apps(small/mid/large) another better for libs maybe.

@deeleman

This comment has been minimized.

Show comment
Hide comment
@deeleman

deeleman Feb 12, 2016

Contributor

My two cents on @c-ice post:

  1. I do prefer shared and it is actually a naming convention that you find quite often in directory structures at many other web environments, easing developers in.
  2. I am not a big fan of appending the type to the filename like todolist.component.ts, but have to recognize that the directory structure you suggest makes a lot of sense to me and it is quite intuitive at a glance. From my own experience, the feature/type pattern (eg src/todo/components/[lotsa files mixed up] - including more type subfolders) scales pretty bad when you have a lot of components in the same level of functionality. Nesting component folders ensures that each folder level will contain a limited amount of files plus other component subfolders, being the latter a good cue of the app hierarchy.

RE models: 100% Agree on this, but why not wrapping all those shared concerns (models, pipes, etc) inside a shared folder?
RE services: I would definitely advocate for a services folder right from day one. It doesn't hurt to have a services folder with one file only and that prevents you from refactoring your import paths as your app eventually grows and you find yourself forced to move service files into a services directory.

Contributor

deeleman commented Feb 12, 2016

My two cents on @c-ice post:

  1. I do prefer shared and it is actually a naming convention that you find quite often in directory structures at many other web environments, easing developers in.
  2. I am not a big fan of appending the type to the filename like todolist.component.ts, but have to recognize that the directory structure you suggest makes a lot of sense to me and it is quite intuitive at a glance. From my own experience, the feature/type pattern (eg src/todo/components/[lotsa files mixed up] - including more type subfolders) scales pretty bad when you have a lot of components in the same level of functionality. Nesting component folders ensures that each folder level will contain a limited amount of files plus other component subfolders, being the latter a good cue of the app hierarchy.

RE models: 100% Agree on this, but why not wrapping all those shared concerns (models, pipes, etc) inside a shared folder?
RE services: I would definitely advocate for a services folder right from day one. It doesn't hurt to have a services folder with one file only and that prevents you from refactoring your import paths as your app eventually grows and you find yourself forced to move service files into a services directory.

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 12, 2016

Contributor

I'm pretty much with @c-ice

I use "common" which imo speaks to the intent of the contents more concisely than "shared" but maybe that's just me.

  • Nesting aside, everything for a component in one place.
  • In a comp. more than X files of type X gets a folder.
  • Services in a separate folder matching feature for more than X files.
  • I do prefer types in file names. Catches my eye better in a tree view for large applications and helpful for fuzzy finding at a higher level.
Contributor

d3viant0ne commented Feb 12, 2016

I'm pretty much with @c-ice

I use "common" which imo speaks to the intent of the contents more concisely than "shared" but maybe that's just me.

  • Nesting aside, everything for a component in one place.
  • In a comp. more than X files of type X gets a folder.
  • Services in a separate folder matching feature for more than X files.
  • I do prefer types in file names. Catches my eye better in a tree view for large applications and helpful for fuzzy finding at a higher level.
@deeleman

This comment has been minimized.

Show comment
Hide comment
@deeleman

deeleman Feb 12, 2016

Contributor

The more I see @c-ice's take on this the more I like it. It would be great to have some more feedback from more people in order to leverage the momentum this discussion has gained and hence reach an agreement fast.

Contributor

deeleman commented Feb 12, 2016

The more I see @c-ice's take on this the more I like it. It would be great to have some more feedback from more people in order to leverage the momentum this discussion has gained and hence reach an agreement fast.

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 12, 2016

Contributor

@deeleman Yeah, imo the direction @c-ice took it resonates with me. I'm going to try it based on one of my production apps so I can see it mocked up at scale.

Contributor

d3viant0ne commented Feb 12, 2016

@deeleman Yeah, imo the direction @c-ice took it resonates with me. I'm going to try it based on one of my production apps so I can see it mocked up at scale.

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 12, 2016

Collaborator

@c-ice @deeleman @d3viant0ne Where a facade is provided, the internal structure of a feature doesn't matter.

As a tie-breaker, the default and most basic use case can just follow what angular-cli already uses.

I suggest this be used as the default recommendation for level 1 complexity.

|-- app
|   |-- hero
|   |   |-- hero-detail.component.html
|   |   |-- hero-detail.component.css
|   |   |-- hero-detail.component.spec.ts
|   |   |-- hero-detail.component.ts
|   |   |-- hero-list.component.html
|   |   |-- hero-list.component.css
|   |   |-- hero-list.component.spec.ts
|   |   |-- hero-list.component.ts
|   |   |-- hero-root.component.spec.ts
|   |   |-- hero-root.component.ts
|   |   |-- hero.service.spec.ts
|   |   |-- hero.service.ts
|   |-- ...
|-- app.ts

For level 2 complexity, the parts can be split into subfolders by type.

├── app
│   ├── todo
│   │   ├── components
│   │   │   ├── todo.view.html ---> [view] for pages
│   │   │   ├── todo.component.ts
│   │   │   └── todo.e2e.ts
│   │   ├── todoItem
│   │   │   ├── todoitem.component.ts
│   │   │   ├── todoitem.html ----> no view its not page
│   │   │   └── todoitem.e2e.ts
│   │   ├── todoList
│   │   │   ├── todolist.component.ts
│   │   │   ├── todolist.html
│   │   │   └── todolist.e2e.ts
│   │   ├── directives
│   │   ├── models
│   │   ├── pipes
│   │   ├── services
│   │   │   └── todo.service.ts
│   │   └──  todo.js <- module facade

Aside: I can't say I'm a huge fan of adding the type to the name. It makes the import statements much longer and the folder tree within an editor much more cluttered. Neither of these issues apply where a facade is used.

For level 3 complexity, the subcomponents can be further broken down with subfolders for each type.


The shared folder should always contain subfolders by type.

├── app
│   ├── shared
│   │   ├── assets
│   │   ├── components
│   │   ├── directives
│   │   ├── models
│   │   ├── pipes
│   │   ├── services
│   │   └──  shared.js <- shared facade

@c-ice Models application-wide models should be included under shared. The major benefit to organization by feature and the shift to web components comes from localizing complexity as much as possible, make it easier to reason about the whole application.

@deeleman @d3viant0ne

  • common vs shared

This is matter of subjective preference, it doesn't really matter what the folder is named. Can you find examples of common being used elsewhere in the NG2 community?

  • Nesting aside, everything for a component in one place.

Exactly, that's the point. Everything else falls into place nicely once if the convention is consistently followed.

  • Services in a separate folder matching feature for more than X files.

Usually, a component will only contain one service and import other application-wide services located under shared. Either way, the service will likely include unit tests.

  • I do prefer types in file names. Catches my eye better in a tree view for large applications and helpful for fuzzy finding at a higher level.

In which editor? Fuzzy finding using [feature-name] [type] works perfectly fine in ST3.

Collaborator

evanplaice commented Feb 12, 2016

@c-ice @deeleman @d3viant0ne Where a facade is provided, the internal structure of a feature doesn't matter.

As a tie-breaker, the default and most basic use case can just follow what angular-cli already uses.

I suggest this be used as the default recommendation for level 1 complexity.

|-- app
|   |-- hero
|   |   |-- hero-detail.component.html
|   |   |-- hero-detail.component.css
|   |   |-- hero-detail.component.spec.ts
|   |   |-- hero-detail.component.ts
|   |   |-- hero-list.component.html
|   |   |-- hero-list.component.css
|   |   |-- hero-list.component.spec.ts
|   |   |-- hero-list.component.ts
|   |   |-- hero-root.component.spec.ts
|   |   |-- hero-root.component.ts
|   |   |-- hero.service.spec.ts
|   |   |-- hero.service.ts
|   |-- ...
|-- app.ts

For level 2 complexity, the parts can be split into subfolders by type.

├── app
│   ├── todo
│   │   ├── components
│   │   │   ├── todo.view.html ---> [view] for pages
│   │   │   ├── todo.component.ts
│   │   │   └── todo.e2e.ts
│   │   ├── todoItem
│   │   │   ├── todoitem.component.ts
│   │   │   ├── todoitem.html ----> no view its not page
│   │   │   └── todoitem.e2e.ts
│   │   ├── todoList
│   │   │   ├── todolist.component.ts
│   │   │   ├── todolist.html
│   │   │   └── todolist.e2e.ts
│   │   ├── directives
│   │   ├── models
│   │   ├── pipes
│   │   ├── services
│   │   │   └── todo.service.ts
│   │   └──  todo.js <- module facade

Aside: I can't say I'm a huge fan of adding the type to the name. It makes the import statements much longer and the folder tree within an editor much more cluttered. Neither of these issues apply where a facade is used.

For level 3 complexity, the subcomponents can be further broken down with subfolders for each type.


The shared folder should always contain subfolders by type.

├── app
│   ├── shared
│   │   ├── assets
│   │   ├── components
│   │   ├── directives
│   │   ├── models
│   │   ├── pipes
│   │   ├── services
│   │   └──  shared.js <- shared facade

@c-ice Models application-wide models should be included under shared. The major benefit to organization by feature and the shift to web components comes from localizing complexity as much as possible, make it easier to reason about the whole application.

@deeleman @d3viant0ne

  • common vs shared

This is matter of subjective preference, it doesn't really matter what the folder is named. Can you find examples of common being used elsewhere in the NG2 community?

  • Nesting aside, everything for a component in one place.

Exactly, that's the point. Everything else falls into place nicely once if the convention is consistently followed.

  • Services in a separate folder matching feature for more than X files.

Usually, a component will only contain one service and import other application-wide services located under shared. Either way, the service will likely include unit tests.

  • I do prefer types in file names. Catches my eye better in a tree view for large applications and helpful for fuzzy finding at a higher level.

In which editor? Fuzzy finding using [feature-name] [type] works perfectly fine in ST3.

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 12, 2016

Contributor

@evanplaice

  • As a tie-breaker, the default and most basic use case can just follow what angular-cli already uses.

Absolutely. I think there is a lot of benefit in the "simplest form" matching the cli. Far easier for people new to the framework to pick up and will also probably aid the adoption of this style guide to a broader audience

  • I can't say I'm a huge fan of adding the type to the name

Converting a large prod app to ng2 this afternoon to apply this conversation to something I am familiar with. I see your point in regards to imports becoming unruly. Point conceded

  • shared / common

It was an ng1 habit I picked up been too long to remember where. Outside of that, it's personal preference and not terribly important other than picking a standard. No issue if it is decided to go with shared.

  • level 2 complexity ( module facade )

I'm fine with doing it this way. Easy enough to reason about and should be simple to pack/bundle with whatever too people decide to use.

  • level 3 complexity

My only concern here would be if it becomes taxing for packing, hot loading, authorization strategies. Not to say I see an issue with it. I think it would be a decent idea for a few of us to take a commonly known project with L3 complexity and apply ng2 / this directory structure / style guide as a real world p.o.c

Essentially take what you have outlined in #10 and apply that to something at reasonable scale to include most of the more advanced tooling we use these days. Proposal would be for you to pick a project and control what lands then we do a before & after. Hopefully it would be more than two of us working on it but it may prove as a selling point to drive this discussion home.

Opinion?

Contributor

d3viant0ne commented Feb 12, 2016

@evanplaice

  • As a tie-breaker, the default and most basic use case can just follow what angular-cli already uses.

Absolutely. I think there is a lot of benefit in the "simplest form" matching the cli. Far easier for people new to the framework to pick up and will also probably aid the adoption of this style guide to a broader audience

  • I can't say I'm a huge fan of adding the type to the name

Converting a large prod app to ng2 this afternoon to apply this conversation to something I am familiar with. I see your point in regards to imports becoming unruly. Point conceded

  • shared / common

It was an ng1 habit I picked up been too long to remember where. Outside of that, it's personal preference and not terribly important other than picking a standard. No issue if it is decided to go with shared.

  • level 2 complexity ( module facade )

I'm fine with doing it this way. Easy enough to reason about and should be simple to pack/bundle with whatever too people decide to use.

  • level 3 complexity

My only concern here would be if it becomes taxing for packing, hot loading, authorization strategies. Not to say I see an issue with it. I think it would be a decent idea for a few of us to take a commonly known project with L3 complexity and apply ng2 / this directory structure / style guide as a real world p.o.c

Essentially take what you have outlined in #10 and apply that to something at reasonable scale to include most of the more advanced tooling we use these days. Proposal would be for you to pick a project and control what lands then we do a before & after. Hopefully it would be more than two of us working on it but it may prove as a selling point to drive this discussion home.

Opinion?

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 13, 2016

Collaborator

@d3viant0ne Just to be clear, the module facade isn't specific to level 2. Using a facade is even more valuable for even larger modules.

The facade should be recommended for:

  • modules of sufficient size
  • modules that will be referenced elsewhere in the project (ie beside the main routing component)
  • modules that will be used externally (the API guide will address this)

Folder structure doesn't reflect application structure. With a facade, the structure of a module is essentially flat, no mater how a module is organized.

I agree that it would be useful to see these practices be put to use. Unfortunately, I don't have anything to apply it to absent starting another project from scratch. Most applications I've seen for NG2 in the wild are really basic.

Collaborator

evanplaice commented Feb 13, 2016

@d3viant0ne Just to be clear, the module facade isn't specific to level 2. Using a facade is even more valuable for even larger modules.

The facade should be recommended for:

  • modules of sufficient size
  • modules that will be referenced elsewhere in the project (ie beside the main routing component)
  • modules that will be used externally (the API guide will address this)

Folder structure doesn't reflect application structure. With a facade, the structure of a module is essentially flat, no mater how a module is organized.

I agree that it would be useful to see these practices be put to use. Unfortunately, I don't have anything to apply it to absent starting another project from scratch. Most applications I've seen for NG2 in the wild are really basic.

@mgechev

This comment has been minimized.

Show comment
Hide comment
@mgechev

mgechev Feb 13, 2016

Owner

Levels of complexity

@evanplaice I like the proposal for a few directory structures based on the app complexity. Three levels of complexity seems reasonable to me and the suggested structures for each of them makes sense.

Definitely it's worth following the angular-cli convention for the first level of complexity.

Type suffix

I'm not huge fan of the type suffix neither. However, we cannot omit it in case we have a couple of different code units with the same name but different type.

In case we don't use type suffix when we spread them among directories for the individual types we loose:

  • Consistency between projects with different scale, directories with different count of components
  • More work for renaming files once moved to code unit-type folder
  • Harder refactoring for some users. Although some editors can perform more sophisticated static code analysis and automatically rename imports in case of change of filename, this is not a common feature for vim for instance.
  • As @d3viant0ne mentioned, fuzzy finding gets trickier

The last two are not very solid arguments.

In case we keep the code unit-type suffix we have more typing/duplication which can be annoying as well. We can weaken the influence of this one by using facade modules.

Although I see more positives of keeping the suffix I'm still not confident that its the correct decision.

Common vs shared

I agree it is more a matter of a personal opinion. Both work for me. Angular 2 uses common internally so we can stick to it eventually.

Models and services directory

I think we should have both from the beginning and they should follow the same convention as everything else. If there are models/services that are unique for a specific module we should keep them there, otherwise move them to higher-level module's models or common.

view suffix for templates of components with @RouteConfig

Makes sense to me. I think we can take it even further and add this suffix even to the controller files. Such components can be used as regular components as well but I don't think this means that we will treat them differently. This way we only append some additional metadata in order to add further semantics on how the component is being used.

What are your thoughts on this?

Others

I agree we should move code units to a separate folder once they get more than X.

Owner

mgechev commented Feb 13, 2016

Levels of complexity

@evanplaice I like the proposal for a few directory structures based on the app complexity. Three levels of complexity seems reasonable to me and the suggested structures for each of them makes sense.

Definitely it's worth following the angular-cli convention for the first level of complexity.

Type suffix

I'm not huge fan of the type suffix neither. However, we cannot omit it in case we have a couple of different code units with the same name but different type.

In case we don't use type suffix when we spread them among directories for the individual types we loose:

  • Consistency between projects with different scale, directories with different count of components
  • More work for renaming files once moved to code unit-type folder
  • Harder refactoring for some users. Although some editors can perform more sophisticated static code analysis and automatically rename imports in case of change of filename, this is not a common feature for vim for instance.
  • As @d3viant0ne mentioned, fuzzy finding gets trickier

The last two are not very solid arguments.

In case we keep the code unit-type suffix we have more typing/duplication which can be annoying as well. We can weaken the influence of this one by using facade modules.

Although I see more positives of keeping the suffix I'm still not confident that its the correct decision.

Common vs shared

I agree it is more a matter of a personal opinion. Both work for me. Angular 2 uses common internally so we can stick to it eventually.

Models and services directory

I think we should have both from the beginning and they should follow the same convention as everything else. If there are models/services that are unique for a specific module we should keep them there, otherwise move them to higher-level module's models or common.

view suffix for templates of components with @RouteConfig

Makes sense to me. I think we can take it even further and add this suffix even to the controller files. Such components can be used as regular components as well but I don't think this means that we will treat them differently. This way we only append some additional metadata in order to add further semantics on how the component is being used.

What are your thoughts on this?

Others

I agree we should move code units to a separate folder once they get more than X.

@mgechev

This comment has been minimized.

Show comment
Hide comment
@mgechev

mgechev Feb 13, 2016

Owner

Just created a gitter chat room.

Owner

mgechev commented Feb 13, 2016

Just created a gitter chat room.

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 13, 2016

Contributor

@mgechev - Type Suffix

in case we have a couple of different code units with the same name but different type

Initially when I read that, i had considered the rule should not be a yes or no but conditional. Essentially Applying the folder concept to type suffixes.

If you have multiple code units of different types, the type suffix is included in the name. That does on the other hand bring up the problem of renaming files already under source control, import refactoring and so on.

  1. I like the idea working on a team because it makes things that much easier when someone is stepping into a new code base.
  2. Not a fan given it just makes import statements longer & chews up space in a tree view.

The first point is personally something I feel is important. Easier to reason about > a few characters saved here and there.

All that said, I think @evanplaice suggestion of using facade modules is the most viable solution. At a glance, it makes the structure easy to reason about without the editor bloat issues. Renaming files would just get frustrating and provide some unwanted side effects in some VCS.

@evanplaice - Sorry, should have been more clear in what I was proposing.

Take a known Angular1x project based on the 1.x style guide, of reasonable complexity and convert it to 2.x using our style guidelines. While this isn't about "upgrading" I think it would be beneficial for most people to see something familiar and then that same project after ng2 & style.

Realistically that is going to be quite a bit or work but in the end, I think the value add to the community would be worth the effort it would take to make it happen. It would also be a really good way to find any holes in the guide & it's recommendations.

Contributor

d3viant0ne commented Feb 13, 2016

@mgechev - Type Suffix

in case we have a couple of different code units with the same name but different type

Initially when I read that, i had considered the rule should not be a yes or no but conditional. Essentially Applying the folder concept to type suffixes.

If you have multiple code units of different types, the type suffix is included in the name. That does on the other hand bring up the problem of renaming files already under source control, import refactoring and so on.

  1. I like the idea working on a team because it makes things that much easier when someone is stepping into a new code base.
  2. Not a fan given it just makes import statements longer & chews up space in a tree view.

The first point is personally something I feel is important. Easier to reason about > a few characters saved here and there.

All that said, I think @evanplaice suggestion of using facade modules is the most viable solution. At a glance, it makes the structure easy to reason about without the editor bloat issues. Renaming files would just get frustrating and provide some unwanted side effects in some VCS.

@evanplaice - Sorry, should have been more clear in what I was proposing.

Take a known Angular1x project based on the 1.x style guide, of reasonable complexity and convert it to 2.x using our style guidelines. While this isn't about "upgrading" I think it would be beneficial for most people to see something familiar and then that same project after ng2 & style.

Realistically that is going to be quite a bit or work but in the end, I think the value add to the community would be worth the effort it would take to make it happen. It would also be a really good way to find any holes in the guide & it's recommendations.

@nareshbhatia

This comment has been minimized.

Show comment
Hide comment
@nareshbhatia

nareshbhatia Feb 13, 2016

Collaborator

By Feature vs. By Type

When reading this thread, I was struggling with questions like "What is a feature?" and "How do I break down my app into a reasonable set of features?". Then I remembered the Domain-Driven Design concept called Bounded Contexts and it helped me figure out how to proceed. From the linked article:

As you try to model a larger domain, it gets progressively harder to build a single unified model...So instead DDD divides up a large system into Bounded Contexts, each of which can have a unified model...Bounded Contexts have both unrelated concepts (such as a support ticket only existing in a customer support context) but also share concepts (such as products and customers).

Armed with this knowledge, a feature is a completely self-supporting unit representing a bounded-context within a large domain. For example, in @evanplaice's site, Thoughts, Designs, Projects and Vitae are completely different contexts and hence can be packaged as different features with their own components, services and domain entities (a.k.a. models). On the other hand, if we had a simple application that dealt with only one concept, say to-dos, then there is no need for an elaborate by-feature directory structure - there is only one! If two features share the same model entities and/or services, then I would question if my partitioning is correct.

Now within a feature, we should look at organizing our code into two areas: the outside and the inside - as suggested by the Hexagonal Architecture. The outside is how the feature interacts with the outside world - the user, the server(s), local storage etc. The inside is the domain entities and the logic encapsulated in and around them. This nicely translates into components and services for the outside and domain or models for the inside. The components folder could be as flat or as nested as desired. My initial approach would be to make it as flat as possible to promote reuse (as suggested by some in this thread), but if the component is very specific to the context, then nesting is probably ok. I don't think there is a right or wrong answer here. It really depends on how "general-purpose" the component is. For example, a date-picker probably does not even belong in a feature folder, it goes in a shared folder somewhere or even better, someone has already written it and you are simply npm installing it!

Hope this helps. Feedback welcome!

Collaborator

nareshbhatia commented Feb 13, 2016

By Feature vs. By Type

When reading this thread, I was struggling with questions like "What is a feature?" and "How do I break down my app into a reasonable set of features?". Then I remembered the Domain-Driven Design concept called Bounded Contexts and it helped me figure out how to proceed. From the linked article:

As you try to model a larger domain, it gets progressively harder to build a single unified model...So instead DDD divides up a large system into Bounded Contexts, each of which can have a unified model...Bounded Contexts have both unrelated concepts (such as a support ticket only existing in a customer support context) but also share concepts (such as products and customers).

Armed with this knowledge, a feature is a completely self-supporting unit representing a bounded-context within a large domain. For example, in @evanplaice's site, Thoughts, Designs, Projects and Vitae are completely different contexts and hence can be packaged as different features with their own components, services and domain entities (a.k.a. models). On the other hand, if we had a simple application that dealt with only one concept, say to-dos, then there is no need for an elaborate by-feature directory structure - there is only one! If two features share the same model entities and/or services, then I would question if my partitioning is correct.

Now within a feature, we should look at organizing our code into two areas: the outside and the inside - as suggested by the Hexagonal Architecture. The outside is how the feature interacts with the outside world - the user, the server(s), local storage etc. The inside is the domain entities and the logic encapsulated in and around them. This nicely translates into components and services for the outside and domain or models for the inside. The components folder could be as flat or as nested as desired. My initial approach would be to make it as flat as possible to promote reuse (as suggested by some in this thread), but if the component is very specific to the context, then nesting is probably ok. I don't think there is a right or wrong answer here. It really depends on how "general-purpose" the component is. For example, a date-picker probably does not even belong in a feature folder, it goes in a shared folder somewhere or even better, someone has already written it and you are simply npm installing it!

Hope this helps. Feedback welcome!

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 13, 2016

Collaborator

@mgechev

Type Suffix

I agree that consistency trumps all. I'm not a fan of the type suffix but that's just personal preference. Since there's justifiable reason to use it for Level 1 complexity, it should be used for all levels of complexity.

I wasn't arguing in favor of renaming and remapping the imports to everything as the level of complexity increases.

Common vs Shared

I have no opinion on this. I chose shared because that's what angular2-seed already uses.

Models and services directory

I agree

view suffix for templates of components with @RouteConfig

Not sure why this is limited to route views. Views in NG2 are applied per-component not per-route as they would be for most MVC frameworks.

One could assume that anything with a .html extension is a view but we may yet see NG2 be extended to support other template formats in the future.

@d3viant0ne By no means do I suggest a structure that requires renaming as it grows. I added the comment as an aside because it doesn't add much value in the bigger picture of things. If the type suffix is used on level 1 complexity, it should be used for all levels of complexity.

I would love the opportunity to convert an AngularJS app to NG2. I don't currently have an opportunity do do so. We'll have to wait and see until somebody does.

Collaborator

evanplaice commented Feb 13, 2016

@mgechev

Type Suffix

I agree that consistency trumps all. I'm not a fan of the type suffix but that's just personal preference. Since there's justifiable reason to use it for Level 1 complexity, it should be used for all levels of complexity.

I wasn't arguing in favor of renaming and remapping the imports to everything as the level of complexity increases.

Common vs Shared

I have no opinion on this. I chose shared because that's what angular2-seed already uses.

Models and services directory

I agree

view suffix for templates of components with @RouteConfig

Not sure why this is limited to route views. Views in NG2 are applied per-component not per-route as they would be for most MVC frameworks.

One could assume that anything with a .html extension is a view but we may yet see NG2 be extended to support other template formats in the future.

@d3viant0ne By no means do I suggest a structure that requires renaming as it grows. I added the comment as an aside because it doesn't add much value in the bigger picture of things. If the type suffix is used on level 1 complexity, it should be used for all levels of complexity.

I would love the opportunity to convert an AngularJS app to NG2. I don't currently have an opportunity do do so. We'll have to wait and see until somebody does.

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 14, 2016

Collaborator

@nareshbhatia

DDD has a lot of good ideas. The issue I find with generalized methodologies is they attempt to define in a single level of granularity, 'one way' to address the problem of complexity. The assumption being that a single 'unified model' is even possible.

There will always be parts of a system that bleed across contexts. The shared/common folder exists as a central location to define application-wide and/or cross-cutting concerns.

What you don't see in the current version of my app are the versions where ng2-markdown and ng2-resume were included as internal features. Both were useful enough that it made sense to extract them out to their own repos and offer them as reusable modules.

On the other hand, if we had a simple application that dealt with only one concept, say to-dos, then there is no need for an elaborate by-feature directory structure

Maybe I should have defined a Level 0 of complexity. For the todo feature, it should be trivial to extract the parts to run as a standalone application (assuming there are no cross-cutting concerns). That's the power of a component-based architecture.

Essentially, a feature is self-contained and the complexity is localized as much as possible. Reusability is a good justification but it's just a side-effect of a system that defines a convention for encapsulation by default.

I would ditch the Hexagonal Architecture altogether. It makes for pretty diagrams but IMHO doesn't add much value. Unless I've missed some key essential benefit, it appears to divide concerns on arbitrary boundaries. That has a lot of potential for bleeding abstractions. I could see some benefit in it's use at the system-level where the system is made up of many microservices, at the application-level I would avoid it.

The inside is the domain entities and the logic encapsulated in and around them. This nicely translates into components and services for the outside and domain or models for the inside.

I don't see any such clear distinction. For instance, a service can be both internal where's it's used to provide and/or cache state and external if it acts as a wrapper for external service calls. Models can be extracted out for use on both client-side and server-side structure/validations. Components can be used internally or extracted out to independent modules for reuse across many different applications. Isomorphic JS may yet add more potential for abstraction breaking characteristics.

For example, a date-picker probably does not even belong in a feature folder, it goes in a shared folder somewhere or even better, someone has already written it and you are simply npm installing it!

A date picker would likely be placed in shared/common. You're right in that it would ideal to install and use a pre-exitsting datepicker.

It's also important to consider the case where somebody is creating new modules. It's not always immediately apparent that a module is a good candidate for reuse. I'm very much a fan of the build-and-extract approach to creating new libraries. Ie start with a 'solution to a problem' rather than a 'solution looking for a problem'.

Starting something like a datepicker as a subfeature, perhaps in a Level 2 complexity directory structure. When it's ready, move it to the shared/components directory to test that all the facade mappings still work. If that's good, extract it into a separate repo and install directly from the repo via NPM/JSPM.

Feel free to share you perspective and correct if I've missed anything.

Collaborator

evanplaice commented Feb 14, 2016

@nareshbhatia

DDD has a lot of good ideas. The issue I find with generalized methodologies is they attempt to define in a single level of granularity, 'one way' to address the problem of complexity. The assumption being that a single 'unified model' is even possible.

There will always be parts of a system that bleed across contexts. The shared/common folder exists as a central location to define application-wide and/or cross-cutting concerns.

What you don't see in the current version of my app are the versions where ng2-markdown and ng2-resume were included as internal features. Both were useful enough that it made sense to extract them out to their own repos and offer them as reusable modules.

On the other hand, if we had a simple application that dealt with only one concept, say to-dos, then there is no need for an elaborate by-feature directory structure

Maybe I should have defined a Level 0 of complexity. For the todo feature, it should be trivial to extract the parts to run as a standalone application (assuming there are no cross-cutting concerns). That's the power of a component-based architecture.

Essentially, a feature is self-contained and the complexity is localized as much as possible. Reusability is a good justification but it's just a side-effect of a system that defines a convention for encapsulation by default.

I would ditch the Hexagonal Architecture altogether. It makes for pretty diagrams but IMHO doesn't add much value. Unless I've missed some key essential benefit, it appears to divide concerns on arbitrary boundaries. That has a lot of potential for bleeding abstractions. I could see some benefit in it's use at the system-level where the system is made up of many microservices, at the application-level I would avoid it.

The inside is the domain entities and the logic encapsulated in and around them. This nicely translates into components and services for the outside and domain or models for the inside.

I don't see any such clear distinction. For instance, a service can be both internal where's it's used to provide and/or cache state and external if it acts as a wrapper for external service calls. Models can be extracted out for use on both client-side and server-side structure/validations. Components can be used internally or extracted out to independent modules for reuse across many different applications. Isomorphic JS may yet add more potential for abstraction breaking characteristics.

For example, a date-picker probably does not even belong in a feature folder, it goes in a shared folder somewhere or even better, someone has already written it and you are simply npm installing it!

A date picker would likely be placed in shared/common. You're right in that it would ideal to install and use a pre-exitsting datepicker.

It's also important to consider the case where somebody is creating new modules. It's not always immediately apparent that a module is a good candidate for reuse. I'm very much a fan of the build-and-extract approach to creating new libraries. Ie start with a 'solution to a problem' rather than a 'solution looking for a problem'.

Starting something like a datepicker as a subfeature, perhaps in a Level 2 complexity directory structure. When it's ready, move it to the shared/components directory to test that all the facade mappings still work. If that's good, extract it into a separate repo and install directly from the repo via NPM/JSPM.

Feel free to share you perspective and correct if I've missed anything.

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 14, 2016

Contributor

@evanplaice

By no means do I suggest a structure that requires renaming as it grows.

I was referencing my original thought above not implying you suggested renaming. In short, i was discounting my original though and agreeing with you.

Contributor

d3viant0ne commented Feb 14, 2016

@evanplaice

By no means do I suggest a structure that requires renaming as it grows.

I was referencing my original thought above not implying you suggested renaming. In short, i was discounting my original though and agreeing with you.

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 14, 2016

Collaborator
Collaborator

evanplaice commented Feb 14, 2016

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 14, 2016

Contributor

@evanplaice @mgechev - Can we lock the API design issue and use that as the "approved" rules?
Issues like this are going to get messy, it will probably be helpful to have a clean reference containing what has been decided that can't be commented on directly.

Contributor

d3viant0ne commented Feb 14, 2016

@evanplaice @mgechev - Can we lock the API design issue and use that as the "approved" rules?
Issues like this are going to get messy, it will probably be helpful to have a clean reference containing what has been decided that can't be commented on directly.

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 14, 2016

Collaborator

@d3viant0ne I'll work on fleshing out the rest. I have 2 more examples to add.

Collaborator

evanplaice commented Feb 14, 2016

@d3viant0ne I'll work on fleshing out the rest. I have 2 more examples to add.

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 14, 2016

Contributor

@evanplaice No worries, just wanted to keep that issue clean if possible as it's a really great start to the project spec.

Contributor

d3viant0ne commented Feb 14, 2016

@evanplaice No worries, just wanted to keep that issue clean if possible as it's a really great start to the project spec.

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 14, 2016

Collaborator

@d3viant0ne I don't have write access so I couldn't lock it, even if I wanted to.

BTW, the API guide has been updated if you'd like to take a look. The next 2 sections are the 'really good' parts.

Collaborator

evanplaice commented Feb 14, 2016

@d3viant0ne I don't have write access so I couldn't lock it, even if I wanted to.

BTW, the API guide has been updated if you'd like to take a look. The next 2 sections are the 'really good' parts.

@nareshbhatia

This comment has been minimized.

Show comment
Hide comment
@nareshbhatia

nareshbhatia Feb 14, 2016

Collaborator

@evanplaice, thanks for your thoughts.

It's not always immediately apparent that a module is a good candidate for reuse. I'm very much a fan of the build-and-extract approach to creating new libraries. Ie start with a 'solution to a problem' rather than a 'solution looking for a problem'.

Really enjoyed reading this perspective.

The issue I find with generalized methodologies is they attempt to define in a single level of granularity, 'one way' to address the problem of complexity. The assumption being that a single 'unified model' is even possible.

Actually DDD and specifically the Bounded Context concept say quite the opposite - we create bounded contexts because it is not always possible to create a single unified model. There may be overlapping domain concepts within two contexts. For example, an Order in an order management system may have a slightly different meaning than an Order in a shipping system. The APIs between these contexts would have to take care of the impedance mismatch. Applying these principles to the discussion at hand helped me a lot. In fact, I was able to better understand why you partitioned your site the way you did.

Collaborator

nareshbhatia commented Feb 14, 2016

@evanplaice, thanks for your thoughts.

It's not always immediately apparent that a module is a good candidate for reuse. I'm very much a fan of the build-and-extract approach to creating new libraries. Ie start with a 'solution to a problem' rather than a 'solution looking for a problem'.

Really enjoyed reading this perspective.

The issue I find with generalized methodologies is they attempt to define in a single level of granularity, 'one way' to address the problem of complexity. The assumption being that a single 'unified model' is even possible.

Actually DDD and specifically the Bounded Context concept say quite the opposite - we create bounded contexts because it is not always possible to create a single unified model. There may be overlapping domain concepts within two contexts. For example, an Order in an order management system may have a slightly different meaning than an Order in a shipping system. The APIs between these contexts would have to take care of the impedance mismatch. Applying these principles to the discussion at hand helped me a lot. In fact, I was able to better understand why you partitioned your site the way you did.

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 15, 2016

Contributor

@evanplaice - I have to agree here, I tend to work the same way. Applications evolve as they and our plans for them are refined.

It's not always immediately apparent that a module is a good candidate for reuse. I'm very much a fan of the build-and-extract approach to creating new libraries. Ie start with a 'solution to a problem' rather than a 'solution looking for a problem'.

Contributor

d3viant0ne commented Feb 15, 2016

@evanplaice - I have to agree here, I tend to work the same way. Applications evolve as they and our plans for them are refined.

It's not always immediately apparent that a module is a good candidate for reuse. I'm very much a fan of the build-and-extract approach to creating new libraries. Ie start with a 'solution to a problem' rather than a 'solution looking for a problem'.

@nareshbhatia

This comment has been minimized.

Show comment
Hide comment
@nareshbhatia

nareshbhatia Feb 15, 2016

Collaborator

Talking about modules and reuse, I do have a question. The module examples I have seen so far use paths relative to the root of the source. This ties the module to the folder structure of the source. Is there a way to specify paths relative to the component? Here's an example from angular2-seed that uses paths relative to the source root:

import {Component} from 'angular2/core';

@Component({
  selector: 'home',
  templateUrl: './home/components/home.html',
  styleUrls: ['./home/components/home.css']
})
export class HomeCmp {}
Collaborator

nareshbhatia commented Feb 15, 2016

Talking about modules and reuse, I do have a question. The module examples I have seen so far use paths relative to the root of the source. This ties the module to the folder structure of the source. Is there a way to specify paths relative to the component? Here's an example from angular2-seed that uses paths relative to the source root:

import {Component} from 'angular2/core';

@Component({
  selector: 'home',
  templateUrl: './home/components/home.html',
  styleUrls: ['./home/components/home.css']
})
export class HomeCmp {}
@nareshbhatia

This comment has been minimized.

Show comment
Hide comment
@nareshbhatia

nareshbhatia Feb 15, 2016

Collaborator

Thanks, @ludohenin - that's very interesting. Does that mean that we can switch to component relative in angular2-seed?

Collaborator

nareshbhatia commented Feb 15, 2016

Thanks, @ludohenin - that's very interesting. Does that mean that we can switch to component relative in angular2-seed?

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 15, 2016

Contributor

@nareshbhatia - Working on the PR for it now

Contributor

d3viant0ne commented Feb 15, 2016

@nareshbhatia - Working on the PR for it now

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 15, 2016

Contributor

@nareshbhatia - For reference, that project is not currently in line with the beginnings of this guide.

While it should & will be updated, I personally think a more real world application example would be a better companion to the style guide.

Contributor

d3viant0ne commented Feb 15, 2016

@nareshbhatia - For reference, that project is not currently in line with the beginnings of this guide.

While it should & will be updated, I personally think a more real world application example would be a better companion to the style guide.

@nareshbhatia

This comment has been minimized.

Show comment
Hide comment
@nareshbhatia

nareshbhatia Feb 15, 2016

Collaborator

Agree!

Collaborator

nareshbhatia commented Feb 15, 2016

Agree!

@d3viant0ne d3viant0ne referenced this issue in mgechev/angular-seed Feb 15, 2016

Closed

Chore - Make template URLs relative to component #485

3 of 3 tasks complete
@mgechev

This comment has been minimized.

Show comment
Hide comment
@mgechev

mgechev Feb 15, 2016

Owner

@nareshbhatia I like how you introduced the idea of bounded context in the discussion. I completely agree, this is the correct way to name it. And again, by using this concept the three levels of complexity that @evanplaice suggested appear very naturally.

As brief (incomplete) recap of the discussion:

  • We create top-level division by-bounded context (for level 1 it is only one, so we can omit it and keep it implicit).
  • Once level 1 overflows we create flat directory structure grouping by-type (@e-oz's suggestion of flat structure fits here).
  • We introduce another level of nesting only in case the complexity grows and it is hard to manage the application.
  • We keep the code unit type suffix for all levels. This helps us by:
    • Making the refactoring easier.
    • Following John Papa's style guide which will make the transition of most AngularJS 1.x apps easier (cc @d3viant0ne).
  • Add facades which brings all the benefits mentioned here #10.
  • Use component relative paths of the associated templates and styles.
Owner

mgechev commented Feb 15, 2016

@nareshbhatia I like how you introduced the idea of bounded context in the discussion. I completely agree, this is the correct way to name it. And again, by using this concept the three levels of complexity that @evanplaice suggested appear very naturally.

As brief (incomplete) recap of the discussion:

  • We create top-level division by-bounded context (for level 1 it is only one, so we can omit it and keep it implicit).
  • Once level 1 overflows we create flat directory structure grouping by-type (@e-oz's suggestion of flat structure fits here).
  • We introduce another level of nesting only in case the complexity grows and it is hard to manage the application.
  • We keep the code unit type suffix for all levels. This helps us by:
    • Making the refactoring easier.
    • Following John Papa's style guide which will make the transition of most AngularJS 1.x apps easier (cc @d3viant0ne).
  • Add facades which brings all the benefits mentioned here #10.
  • Use component relative paths of the associated templates and styles.
@nareshbhatia

This comment has been minimized.

Show comment
Hide comment
@nareshbhatia

nareshbhatia Feb 15, 2016

Collaborator

Thanks, @mgechev. Nicely summarized!

Collaborator

nareshbhatia commented Feb 15, 2016

Thanks, @mgechev. Nicely summarized!

@juristr

This comment has been minimized.

Show comment
Hide comment
@juristr

juristr Feb 15, 2016

@mgechev I'm late to the party, but fully agree on the outcome. Nice recap.

@d3viant0ne FYI: There's an issue open for using relative parts with SystemJS: angular/angular#6053

juristr commented Feb 15, 2016

@mgechev I'm late to the party, but fully agree on the outcome. Nice recap.

@d3viant0ne FYI: There's an issue open for using relative parts with SystemJS: angular/angular#6053

@d3viant0ne

This comment has been minimized.

Show comment
Hide comment
@d3viant0ne

d3viant0ne Feb 15, 2016

Contributor

@juristr module.id still works. Once __moduleName is fixed, i'll have to update it again.

Contributor

d3viant0ne commented Feb 15, 2016

@juristr module.id still works. Once __moduleName is fixed, i'll have to update it again.

@e-oz

This comment has been minimized.

Show comment
Hide comment
@e-oz

e-oz Feb 15, 2016

thank you very much for taking my opinion into consideration

e-oz commented Feb 15, 2016

thank you very much for taking my opinion into consideration

@evanplaice

This comment has been minimized.

Show comment
Hide comment
@evanplaice

evanplaice Feb 16, 2016

Collaborator

@juristr Haha, you beat me to it. I've been watching the progress on relative template/style paths since beta.0.

@d3viant0ne You mention that a fix to make System.js work with module.id. Do you have a source for the fix? I'd really like to see how this can be done. I have a workaround that uses the plugin-text extension of JSPM but it doesn't work with minify/uglify.

Collaborator

evanplaice commented Feb 16, 2016

@juristr Haha, you beat me to it. I've been watching the progress on relative template/style paths since beta.0.

@d3viant0ne You mention that a fix to make System.js work with module.id. Do you have a source for the fix? I'd really like to see how this can be done. I have a workaround that uses the plugin-text extension of JSPM but it doesn't work with minify/uglify.

@nareshbhatia

This comment has been minimized.

Show comment
Hide comment
@nareshbhatia

nareshbhatia Feb 20, 2016

Collaborator

@mgechev, can we update the style guide for at least the naming of the component files so that we can go ahead with mgechev/angular-seed#486. Specifically, component file names should be component-name + .component.ts, e.g. about.component.ts. Also let's change all the examples to conform to this convention.

P.S. I think other filenames are ok because the file extensions indicate the type of the file - .html, .css etc.

Collaborator

nareshbhatia commented Feb 20, 2016

@mgechev, can we update the style guide for at least the naming of the component files so that we can go ahead with mgechev/angular-seed#486. Specifically, component file names should be component-name + .component.ts, e.g. about.component.ts. Also let's change all the examples to conform to this convention.

P.S. I think other filenames are ok because the file extensions indicate the type of the file - .html, .css etc.

@mgechev

This comment has been minimized.

Show comment
Hide comment
@mgechev

mgechev Feb 20, 2016

Owner

@nareshbhatia yes, I'll do that in an hour.

Owner

mgechev commented Feb 20, 2016

@nareshbhatia yes, I'll do that in an hour.

@maikokuppe

This comment has been minimized.

Show comment
Hide comment
@maikokuppe

maikokuppe Feb 24, 2016

Right now the index.html is placed in the app/ folder. I suggest to place it in the root folder for the following reasons:

JS/CSS dependencies (this includes angular itself) are included by <script> tags in index.html.
If dependencies are in the root folder, these tags will contain a .. in their paths, i. e.

<script src="../node_modules/angular2/bundles/angular2-all.umd.min.js"></script>

This probably won't work on production unless the root directory is accessed by the app's root url myapp.com which implies that the app itself can only be accessed by myapp.com/app. Nobody wants this as an app's base url.

If dependencies are in the app folder, it will work fine, but then we got the dependency folders (node_modules, bower_components, ...) in there, too. Configuring grunt or gulp compile tasks now becomes harder: Let's consider a coffeescript compiler (same holds for others). Instead of letting it compile app/**/*.coffee files, now we have to specify the names of the subfolders within the app/ directory, which will change over time. If we don't do that, our compiler would compile any *.coffee file in our dependencies.

Moving index.html one directory up would solve these problems. I agree that it is part of the app and belongs into the app/ directory somehow, but it's still an index.html which is usually found in the root directory, so this is a pretty natural placement.

Right now the index.html is placed in the app/ folder. I suggest to place it in the root folder for the following reasons:

JS/CSS dependencies (this includes angular itself) are included by <script> tags in index.html.
If dependencies are in the root folder, these tags will contain a .. in their paths, i. e.

<script src="../node_modules/angular2/bundles/angular2-all.umd.min.js"></script>

This probably won't work on production unless the root directory is accessed by the app's root url myapp.com which implies that the app itself can only be accessed by myapp.com/app. Nobody wants this as an app's base url.

If dependencies are in the app folder, it will work fine, but then we got the dependency folders (node_modules, bower_components, ...) in there, too. Configuring grunt or gulp compile tasks now becomes harder: Let's consider a coffeescript compiler (same holds for others). Instead of letting it compile app/**/*.coffee files, now we have to specify the names of the subfolders within the app/ directory, which will change over time. If we don't do that, our compiler would compile any *.coffee file in our dependencies.

Moving index.html one directory up would solve these problems. I agree that it is part of the app and belongs into the app/ directory somehow, but it's still an index.html which is usually found in the root directory, so this is a pretty natural placement.

@mgechev mgechev reopened this Feb 25, 2016

@mgechev mgechev closed this Feb 25, 2016

@mgechev

This comment has been minimized.

Show comment
Hide comment
@mgechev

mgechev Feb 25, 2016

Owner

@maikokuppe I don't think we've mentioned index.html anywhere.

The index.html file is located under src in angular2-seed because of the way we've developed the build but in the style guide we don't specify the location where the index file should be.

You're free to located either in src or in the root of your project for easier access to node_modules.

Owner

mgechev commented Feb 25, 2016

@maikokuppe I don't think we've mentioned index.html anywhere.

The index.html file is located under src in angular2-seed because of the way we've developed the build but in the style guide we don't specify the location where the index file should be.

You're free to located either in src or in the root of your project for easier access to node_modules.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment