Skip to content

Application decoupling #913

New issue

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

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

Already on GitHub? Sign in to your account

Closed
mgechev opened this issue May 23, 2016 · 54 comments
Closed

Application decoupling #913

mgechev opened this issue May 23, 2016 · 54 comments

Comments

@mgechev
Copy link
Owner

mgechev commented May 23, 2016

Purpose

Allow easier development of multi-platform applications with easy code reuse across them.

Make the entire client-side application and its dependencies, decoupled from the build process and encapsulated inside src/client.

Background

At the moment we have our client application in the src/client directory and the build-related tasks in tools. Although we have this separation, all the client-side related dependencies and testing configuration are global (test-main.js, karma.config.js, protractor.config.js).

This makes it unpractical to use the seed for development of multiple Angular applications for different platforms which are sharing common features.

For instance, in case we want to develop backend for the current application, which reuses some of the logic which is already defined by services in the client-side, we need to:

  • Store all the server-side related dependencies in the package.json file where we have the tooling related dependencies and client side related dependencies.
  • Have another testing configuration inside src/server.

This approach is not modular and has several drawbacks:

  • If we need to build only the backend we need to install the dependencies for the backend, client and tooling.
  • The configuration is not encapsulated and it could be confusing to have different versions of the same configuration files (for client in the root directory and for the server in src/server).

Proposal

  • Move all the files which are related to client-side configuration and logic to src/client.
  • Have a single build for each individual application (i.e. leave the current build organization unchanged).
  • Leave all build related dependencies in the package.json in the root of the project and move all others to src/client/package.json.
.
├── appveyor.yml
├── gulpfile.ts
├── src
│   └── client
│       ├── dist
│       ├── coverage
│       ├── app
│       ├── assets
│       ├── css
│       ├── index.html
│       ├── test-main.js
│       ├── tsconfig.json
│       ├── karma.conf.js
│       ├── package.json
│       ├── protractor.conf.js
│       └── typings.d.ts
├── tools
├── tsconfig.json
├── tslint.json
├── typings
├── package.json
└── typings.json

@NathanWalker's advanced seed should benefit a lot from the proposal above.

// cc @ludohenin @d3viant0ne @TheDonDope @nareshbhatia

@TheDonDope
Copy link
Contributor

Hi all!

I really like the proposal, it does make a lot of sense to relocate this client specific to this more appropiate place.

I agree, the advanced seed of @NathanWalker would benefit from this refactor. One point still sticks out for me: shouldn't the client directory get a more specific name? Maybe ng2-clientor something like this? If the approach is to enable multiplatform/multiclient apps, how would one differentiate between the different clients? But really, that is just a minor issue :)

@mgechev
Copy link
Owner Author

mgechev commented May 23, 2016

May be web-client?

@NathanWalker
Copy link
Collaborator

NathanWalker commented May 23, 2016

Great proposal @mgechev.
How about just web?

Then you could have something like following for all sorts of scalability:
I feel as the naming should be very platform specific and stay away from things like client or frontend since we truly are in the new brave world of true cross platform development with Angular 2.

.
├── appveyor.yml
├── gulpfile.ts
├── src
│   └── desktop
│   └── reactnative
│   └── nativescript
│   └── server
│   └── web
│       ├── dist
│       ├── coverage
│       ├── app
│       ├── assets
│       ├── css
│       ├── index.html
│       ├── test-main.js
│       ├── tsconfig.json
│       ├── karma.conf.js
│       ├── package.json
│       ├── protractor.conf.js
│       └── typings.d.ts
├── tools
├── tsconfig.json
├── tslint.json
├── typings
├── package.json
└── typings.json

@joshwiens
Copy link
Contributor

IMO "web" would be the most explicit without getting into something platform specific or non-descript like "public".

Call it web / browser but it's probably best to keep it's naming related to the platform being used for delivery like the rest.

@NathanWalker
Copy link
Collaborator

I like browser too @d3viant0ne Either way.

@NathanWalker
Copy link
Collaborator

What's nice about this direction is the platform specific naming conventions can be left for the user to decide:

.
├── appveyor.yml
├── gulpfile.ts
├── src
│   └── desktop  <-- could be electron or something else.
│   └── mobile <-- could be nativescript, reactnative, or something else.
│   └── server
│   └── web
│       ├── dist
│       ├── coverage
│       ├── app
│       ├── assets
│       ├── css
│       ├── index.html
│       ├── test-main.js
│       ├── tsconfig.json
│       ├── karma.conf.js
│       ├── package.json
│       ├── protractor.conf.js
│       └── typings.d.ts
├── tools
├── tsconfig.json
├── tslint.json
├── typings
├── package.json
└── typings.json

@TheDonDope
Copy link
Contributor

web sounds good to me too!

@mgechev
Copy link
Owner Author

mgechev commented May 23, 2016

Ok, let's call it web.

@arm5472
Copy link

arm5472 commented May 24, 2016

Great idea @mgechev. This decoupling makes perfect sense. I also like web.

@joshwiens
Copy link
Contributor

joshwiens commented May 24, 2016

Not that I want to debate it endlessly but if we are naming things based on the medium used as a content delivery method, browser is technically more accurate.

@ludohenin
Copy link
Collaborator

I vote for browser as well and second @d3viant0ne

@ludohenin
Copy link
Collaborator

... but the proposal rocks.

@NathanWalker
Copy link
Collaborator

NathanWalker commented May 24, 2016

It's a good debate and naming is always hard.

browser cons:

  • what is built by that folder is for the web, which may not technically be a browser. for instance, a hybrid app may use that built output from that folder in a webview in a hybrid app. in that sense, browser doesn't apply
  • browser is a type of medium that delivers the web and therefore browser is not a platform, but rather web is.
  • browser is not the same canonical meaning as desktop or mobile since to build for desktop comes in many forms and delivery vehicles (one of which could be through a browser, but also could be in a .NET form, or a Cocoa app)
  • browser is an app itself and therefore not a platform.

Platform examples: web, mobile, desktop, server, tv, xbox

Angular and React are vehicles to develop for all platforms above.
NativeScript and ReactNative are vehicles to develop for the mobile platform.
Electron and NW.js are vehicles to develop for the desktop platform.
Express, PHP, and Ruby on Rails are vehicles to develop for the server platform.

Then there are the crossover vehicles like Ionic and Cordova which can be used to develop for web and mobile platforms. This is where browser is ruled out as a platform since to say that Ionic is a vehicle for the browser is true but not definitive because browser is not a platform, but to say Ionic is a vehicle for the web platform is accurate since it can be used to develop for the browser and for a hybrid app because it definitively is a vehicle for the web platform.

Just my 2 cents.

@joshwiens
Copy link
Contributor

Platform examples: web, mobile, desktop, server, tv, xbox

It really comes down to how what and how you think about delivery mediums. To me web is a generic term that encompasses the interconnects between delivery mediums of which something like the Chrome web browser is one.

In the end naming this is funky now because the lines between delivery mediums has been blurred significantly, which is only going to get worse.

We could ask 100 people and it would probably be split down the middle for web / browser. In the end I think either choice conveys the intent well enough to get the purpose across.

Either way, imo this refactoring idea is probably the most beneficial out of any that have landed. You can call it purple fluffy bunnies so long as the change lands in #master :)

@ludohenin
Copy link
Collaborator

Just to bring extra water to the mill 😄(I want to see browser win) (source wikipedia)

web

The World Wide Web (WWW) is an information space where documents and other web resources are identified by URLs, interlinked by hypertext links, and can be accessed via the Internet.

browser

A web browser (commonly referred to as a browser) is a software application for retrieving, presenting, and traversing information resources on the World Wide Web.

That's pretty straight forward, isn't it ?
The entire world is probably wrong calling apps web applications which are in fact browser applications.
Shit, we have to tell the entire world now :)

@joshwiens
Copy link
Contributor

I'll create the issue to inform the planet and just go ahead and assign that one to you @ludohenin . If you could get on that right after the whole offline compile feature, that would be great...thanks.

@Shyam-Chen
Copy link
Contributor

Why not web? If integration Material and Firebase be weird.

@TheDonDope
Copy link
Contributor

TheDonDope commented May 26, 2016

Just as @d3viant0ne said:

We could ask 100 people and it would probably be split down the middle for web / browser.

To add onto that, i'd like to quote Phil Karlton:

There are only two hard things in Computer Science: cache invalidation and naming things.

The corresponding PR isn't finished, so there could still be changes, and though i personally tend towards web too, but i went ahead with browser because i flipped a coin with web being heads and browser being tails, and tails showed.

two_face_6711

TheDonDope pushed a commit that referenced this issue Jun 3, 2016
- Implements the application decoupling as discussed in #913.
- Excludes karma.conf.js, protractor.conf.js and test-main.js from the copy
build task, so that those files will not be copied over to /dist/** directory.
Furthermore also excludes the tsconfig.json from this copy process, as it was
copied over before too.
- Updates the protractor configuration and associated gulp task.
- Adds a e2e.singleRun gulp task to the gulpfile to leave the script
in the package.json more compact. Also updates the README to include
the new folder and file structure.
@spicemix
Copy link

spicemix commented Jun 5, 2016

How about a src/shared directory for platform-independent/isomorphic core code that can be unit tested/profiled on its own?

@NathanWalker
Copy link
Collaborator

NathanWalker commented Jun 5, 2016

@spicemix I really like the thought of that 👍 There will certainly be some universal/isomorphic code with this kind of setup.

@NathanWalker
Copy link
Collaborator

@TheDonDope I'll be using web in advanced seed so whatever you guys wanna use here, makes no difference to me :)

@joshwiens
Copy link
Contributor

@TheDonDope - Are you still stuck on the what package goes where step to all of this?

If so, i'll PR the changes into your current working branch on your fork so we can bring this one home.

@wolfganga
Copy link

wolfganga commented Jul 21, 2016

Since this topic is still under discussion, i want to propose to think about a workspace like structure to allow multiple projects to share one installation.

With the current proposal, the decoupling of a custom project and the default seed project looks like:

├── appveyor.yml
├── gulpfile.ts
├── src
│   └── client  <-- project base of the default seed project
│       ├── dist
│       ├── coverage
│       ├── app
│       ├── assets
│       ├── css
│       ├── index.html
│       ├── test-main.js
│       ├── tsconfig.json
│       ├── karma.conf.js
│       ├── package.json
│       ├── protractor.conf.js
│       └── typings.d.ts
│   └── myproject  <-- project base of "myproject"
│       ├── dist
│       ├── coverage
│       ├── app
│       ├── assets
│       ├── css
│       ├── index.html
│       ├── test-main.js
│       ├── tsconfig.json
│       ├── karma.conf.js
│       ├── package.json
│       ├── protractor.conf.js
│       └── typings.d.ts
├── tools
├── tsconfig.json
├── tslint.json
├── typings
├── package.json
└── typings.json

To use it: npm start -- --client myproject
This makes updating the seed itself much easier while working on the custom project.

Now looking to the advanced seed project, with the change proposed / agreed to by NathanWalker, the purpose of that client directory is somehow different. It is no longer a project container on its own but a target within the same project. Think of src/ as the project base dir, where in the previous example client/ was the project base dir.

├── appveyor.yml
├── gulpfile.ts
├── src <--  project base
│   └── desktop  <-- project target within the same project - could be electron or something else.
│   └── mobile <-- project target within the same project - could be nativescript, reactnative, or something else.
│   └── server
│   └── web
│       ├── dist
│       ├── coverage
│       ├── app
│       ├── assets
│       ├── css
│       ├── index.html
│       ├── test-main.js
│       ├── tsconfig.json
│       ├── karma.conf.js
│       ├── package.json
│       ├── protractor.conf.js
│       └── typings.d.ts
├── tools
├── tsconfig.json
├── tslint.json
├── typings
├── package.json
└── typings.json

An integration of myproject would be difficult, it would be on the same level as the project targets for desktop, mobile etc.

So why not allowing both, an individuall project with its specific targets within one common workspace:

├── appveyor.yml
├── gulpfile.ts
├── workspace <-- new 
│   └── seed  <-- basedir of the seed project istelf - set as default (former client/ directory)
│       └── desktop  <-- target within this project - could be electron or something else.
│       └── mobile <-- target within this project - could be nativescript, reactnative, or something else.
│       └── server
│       └── web
│           ├── dist
│           ├── coverage
│           ├── app
│           ├── assets
│           ├── css
│           ├── index.html
│           ├── test-main.js
│           ├── tsconfig.json
│           ├── karma.conf.js
│           ├── package.json
│           ├── protractor.conf.js
│           └── typings.d.ts
│   └── myproject <-- basedir of my project - refers to the project config like in tools/config/project.config.ts
│       └── desktop  
│       └── mobile 
│       └── server
│       └── web
│           ├── dist
│           ├── coverage
│           ├── app
│           ├── assets
│           ├── css
│           ├── index.html
│           ├── test-main.js
│           ├── tsconfig.json
│           ├── karma.conf.js
│           ├── package.json
│           ├── protractor.conf.js
│           └── typings.d.ts
├── tools
├── tsconfig.json
├── tslint.json
├── typings
├── package.json
└── typings.json

Maybe this can be even extended to allow more than one custom project. (Granted, this would need more thoughts on how to setup the individuall project settings and the build process.)

@DmitryEfimenko
Copy link

that's a shame.
Thanks for pointing out to the express seed. Though there is still a lot of work need to be done there to properly support server.

@vyakymenko
Copy link
Contributor

@DmitryEfimenko , in progress :))

@DmitryEfimenko
Copy link

how do we get this issue going? I saw @TheDonDope 's branch. It does not look like it's too much work and I do believe that proper project structure is one of the most important things.

@TheDonDope, were you close to being done with it? If so I could quickly repeat changes made by you and submit a PR against latest code

@TheDonDope
Copy link
Contributor

@DmitryEfimenko
It is not as simple as one may think.
First of all i urge you to take a few minutes and have a look at #959, especially the comment section starting at #959 (comment)

The initial work that was done was only the tip of the iceberg, if you read through the comments you will see that there is a lot to be done ahead, and there already was a lot of time invested into this. So there will be no "quick finish" for this.

Basically, one should take a look at the current status and come up with a detailed concept of things to be done still. Because the complexity is high enough, that one cannot simply start working on without beforehand analysis.

Sorry, i don't want to be a downer here, just trying to manage expectations!

Alas, if you still are on fire and want to work on this, after reading all the beforehand stuff, i wholeheartedly welcome you to open a new issue to get things started again, maybe starting to work on the analysis part.

Cheerio

Dope

@e-oz
Copy link
Contributor

e-oz commented Aug 16, 2016

in case we want to develop backend for the current application, which reuses some of the logic which is already defined by services in the client-side, we need to:

Move this code to reusable library and forget about multiple apps inside one repo, it's insanity.

@e-oz
Copy link
Contributor

e-oz commented Aug 16, 2016

Not sure why 'client' folder exist, if it's the only folder in src. Just extra naming.

@mgechev
Copy link
Owner Author

mgechev commented Aug 16, 2016

Yeah, at this point I agree that it might be a better idea to drop the client directory.

@DmitryEfimenko
Copy link

Disagree completely.
There are almost no projects out there that do not have server. As @TheDonDope pointed out, angular2-seed-express is a great example of creating a project that has both server and client sides.
I understand that this particular seed currently has only angular in the source code, but it might be beneficial to actually create a simple node server as a starting point. It would be easy enough to enchant/ delete or replace it with something else.

@e-oz
Copy link
Contributor

e-oz commented Aug 16, 2016

It's weird to think all projects use nodejs as a server.

@e-oz
Copy link
Contributor

e-oz commented Aug 16, 2016

And server is a completely different app with completely different dependencies. It's really weird way to reuse code by pushing everything into one huge mess - usually for code reuse people do exactly opposite - split code to reusable parts and move them out as libraries/modules/etc.

@vyakymenko
Copy link
Contributor

vyakymenko commented Aug 16, 2016

@e-oz , okay, in the production build client-side app will be built to dist/prod folder, so what is the problem with client folder? What if you will use Python, Java, .NET, that will look for client folder changes, docker for deploy with precompile and you want to operate with dual ts compilation for many apps or parts.CLI(any that you like) for watcher testings for specific folders.Daemon that will watch to some directories that you want aka PM2, and you can easily use ESBas pattern for extra-large applications, you can without any problems add another folder for another app, with one compiler and precompiler(AOT) ?

@e-oz
Copy link
Contributor

e-oz commented Aug 16, 2016

@vyakymenko , maybe because 'dist' should be cleaned before build. Or maybe because you can configure target (output) dir for build in gulpfile, without adding any additional folders to source.

@vyakymenko
Copy link
Contributor

@e-oz , it's always cleaned:

gulpfile.ts

// --------------
// Build prod.
gulp.task('build.prod', (done: any) =>
  runSequence('clean.prod', // <----- @e-oz, clean folder
              'tslint',
              'css-lint',
              'build.assets.prod',
              'build.html_css',
              'copy.client.js.prod',
              'copy.server.js.prod',
              'build.js.prod',
              'build.bundles',
              'build.bundles.app',
              'build.index.prod',
              'build.server.prod',
              'copy.server.assets',
              done));

seed.config.ts and project.config.ts

/**
   * The directory where the client files are located.
   * The default directory is `client`.
   * @type {string}
   */
  APP_CLIENT = argv['client'] || 'whiskas';

// @e-oz, or add you own

@e-oz
Copy link
Contributor

e-oz commented Aug 16, 2016

@vyakymenko that's what I'm talking about. If dist will contain other folders, from other projects, they will be removed also.

@vyakymenko
Copy link
Contributor

@e-oz , if you will configure your own task with another folder structure in project.config.ts or seed.config.ts everything will be okay. Look for example with angular2-seed-express or adnvanced-angular2-seed. You can configure you folders copy, and cleaning. It depends on your specific technical issue, project structure/architecture.

And keep in mind, that all new/newbie developers will search that client source much longer. Remember about one of 7 important development paradigm KISS.

@e-oz
Copy link
Contributor

e-oz commented Aug 16, 2016

@vyakymenko in tools/config/seed.config.ts you can change DIST_DIR = 'dist'; or PROD_DEST to any exotic path for any exotic watcher. I think it's better than force everybody else to have client folder just because of exotic watchers.

And keep in mind, that all new/newbie developers will search that client source much longer.

Not sure what you are talking about. New developers are writing client-side app, so everything in src is client by default. Making them think "there should be something else besides client" will not make their life easier.

@vyakymenko
Copy link
Contributor

vyakymenko commented Aug 16, 2016

@e-oz , If you try to learn gulp, you need to learn Node.js a little bit?
Dual-app, client for users and admin for admins, with different bootstraps but one angular2-seed builder?

New developers are writing client-side app, so everything in src is client by default

And injections, shims etc ?

P.S. Don't want to start a holy war.

@e-oz
Copy link
Contributor

e-oz commented Aug 16, 2016

Not sure want you want to prove. You want to add knowledge of nodejs (and ability to write dual-app code) as a requirement to use this seed? It's against that KISS principle. If not say more.

I think I not only argumented my opinion but also found simple workaround for you, so I don't think I can add something to this talk.

@tarlepp
Copy link
Contributor

tarlepp commented Aug 16, 2016

@DmitryEfimenko you really want to add server side? If yes seed should have every server side options to satisfy everyone (C#, Python, Java, PHP, node, etc.). I don't really see point to take that path.

@vyakymenko
Copy link
Contributor

@e-oz , I want to say, that we have client folder with one Angular2 application. And if you will want, you can feel free just to add another application to the src folder and start to work with another application based on this seed.
Because my logic is that this is a seed, when we use water seed grows to tree with one or more applications. Does that make sense?

@e-oz
Copy link
Contributor

e-oz commented Aug 16, 2016

@vyakymenko yes, but different apps have different dependencies, and you have one package.json. Every version change will be nightmare. Having different apps in one repo is a very bad idea.

@tarlepp
Copy link
Contributor

tarlepp commented Aug 16, 2016

And what if water app support is gone but tree still grows? Are you really going to make water app all those necessary changes? Just asking who is going to pay that "extra" work?

@vyakymenko
Copy link
Contributor

@tarlepp , tree will die without water it's an axiom, doesn't matter what tree. Good example for "extra" work, any retailer-manufacture app that can use dual-apps.

@vyakymenko
Copy link
Contributor

vyakymenko commented Aug 16, 2016

@e-oz , I agree with you. But for sometimes, it's a must.

P.S. I never use dual-apps, but I think that it can be.

@tarlepp
Copy link
Contributor

tarlepp commented Aug 16, 2016

@vyakymenko "extra" work is eg. when seed updates to newer angular which will broke something. And this goes when there is water, tree1 and tree2 and eg. tree1 project dies on your hand.

@vyakymenko
Copy link
Contributor

vyakymenko commented Aug 16, 2016

@tarlepp , your tree it's a seed project, and folder's like client it's a branch(some first app with same dependencies), then you add another branch, folder fo example admin, and we have one tree with 2 branches.

But, I agree with your point !

For sometimes, tree can be.

@vyakymenko
Copy link
Contributor

vyakymenko commented Aug 17, 2016

@tarlepp , I was thinking about your point that you told in latest comment and have some real-world example of this seed vision.
One project --> One seed, it's a must for good project support.
One project can have multi-applications, real example:

  • We need to create for Tetris game.
  • It can have free and pay versions, all of them based on free version.
  • Pay version have some additional features, more figures and no advertising.

So, we will have 2 folders client, client-pay for example.
And by the way, the client-pay can import, extend client(base) functionality, and we can add another feature for pay version.
We can update client(free) core for 2 applications.

So we build one project(angular2-seed) with dual-application.

@jerryorta-dev
Copy link
Contributor

jerryorta-dev commented Sep 5, 2016

Idea 1:
The SystemJS builder requires a config and the main.ts file. It recursively looks for all the imports, then using rollup and tree shaking to create the build. You can use this feature to have multiple app files -- mainTetrisFree.ts, mainTetrisPay.ts -- to create different builds. Each main file can import the proper feature set -- free or pay -- as needed and a gulp task wrapper to support each.

Idea 2:
SystemJS supports conditional loading. You can import the appropriate feature based on environment flags.

Idea 3:
In conjunction with the ideas above, features can be shared via namespaced npm modules.

Any idea requires you to contend with the <%= SOME_VARIABLE %> variables.

@jerryorta-dev
Copy link
Contributor

jerryorta-dev commented Sep 10, 2016

My fork now has an example of SystemJS Conditional Substitution feature -- loading/building and app to include featureA or featureB based on configuration. This is just a PoC to see how you can handle decoupling with SystemJS.

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

No branches or pull requests