Previously I have presented MVC - Model: the great forgotten, in which I have explained what models were for.
It was very well received but because it was focused only in the model concept it leak of some important concepts about the whole MVC abstraction or how it make scale into a large AngularJS.
In the previous talk I gave some hints, but not all, and the audience asked for more.
This presentation explains the whole MVS, and adaptation of MVC leveraging in Angular capacities.
It should be presented in the Angular Camp of July 2016, but unfortunately their organizers forgot about it in the votations for talk and different set of talks were selected from the same author.
This talk has been given to some of my customers in private sessions.
2. THINGS WRONG THAT I DID TO FIT THINGS IN SLIDES
DISCLAIMER
• Directives should use controller and bindToController
• Directives should never load resources, this should be done by
routes or under demand
• Use .factory to simplify and homogenize code
(not .service neither .value)
• In a previous talk I said that model was plain JS data, sorry
(plain data is the form, but does not defines a model)
• Use better variable names
23. NEW FEATURE - UPDATE BOOK
SHIT!
What about:
• Cache?
• What if we have a list side-by side, it is updated?
• What if we have view and update side-by-side?
24. NEW FEATURE - UPDATE BOOK
SHIT!
How to solve it?
• New services?
• Callbacks?
• RxJS?
• More caches?
• Events?
26. NEW FEATURE - UPDATE BOOK
WHAT IS THE REAL PROBLEM?
R4 (manage caches) is hidden another responsibility:
• Single source of true
We need a model!
27. WHAT A MODEL IS NOT?
• A model is not styles in the web
• A model is not objects in the scope
• A model is not objects stored in the browser (local,websql,…)
• A model are not instances of any sort of data
• A model is not plain data
28. SINGLE SOURCE OF TRUE
WHAT IS A MODEL?
• You can find your model
• There is only one instance for each “concept”
(all books with the same id are the same object)
• Models may be computed from other models
• It does not matters how many views do you have,
all views use the same models
(as do services also)
That is something that ember programmers knows very well.
29. REFACTOR - ADD MODEL
Book
app.value(‘Book’, function Book(id) {
this.id = id;
this.isSciFi = function() { return !!this.tags.sciFi; };
this.takeData = function(bookData) {
angular.extend(this, bookData);
};
});
We discover new responsibilities:
• R5: update and extract information of a book
30. REFACTOR - ADD MODEL
booksDictionary
app.value(‘booksDictionary’, (Book) => {
this.list = []; this.map = {};
this.getOrCreate = (id) => {
var book = this.map[id];
if (!book) {
book = new Book(id);
this.list.push(book); this.list.map[id] = book;
};
return book;
};
this.takeDatas = (bookDatas) => {
bookInfos.forEach((bookData) => {
this.getOrCreate(bookData.id).takeData(bookData);
});
};
});
We discover new responsibilities:
• R6: manage book instances to avoid replicates
• R7: store books so anyone can access to them
Note that list
is also a model.
31. booksService (loadAll)
app.service(‘booksService’, (booksDictionary, $http) => {
this.getList = () => {
if (!this.list) {
this.list = $http.get(‘/api/v1/books’).then(response => {
booksDictionary.takeDatas(response.data);
return booksDictionary.list;
});
}
return this.list;
});
};
…
});
REFACTOR - ADD MODEL
getList returns always the same instance of list.
32. booksService (load)
app.service(‘booksService’, (booksDictionary, $http) => {
…
this.books = {};
this.load = (id) => {
if (!this.books[id]) {
this.books[id] = $http.get(‘/api/v1/books/’+id).then(response => {
var book = booksDictionary.getOrCreate(id);
book.takeData(reponse.data);
return book;
});
}
return this.books[id];
};
…
});
REFACTOR - ADD MODEL
Load(id) returns always the same instance of book for the same id.
And the instance will be contained by the list in getList.
33. booksService (update)
app.service(‘booksService’, (booksDictionary, $http) => {
…
this.update = (book) => {
var id = book.id;
this.books[id] = $http.put(‘/api/v1/books/’+id).then(response => {
var book = booksDictionary.getOrCreate(id);
book.takeData(response.data);
return book;
});
};
});
REFACTOR - ADD MODEL
Update returns always the same instance than get and getList,
regardless the input. And also updates the same instance.
36. NEW FEATURE - UPDATE BOOK
DONE?
What about Sci-Fi list?
• Is computed:
it has to be recomputed each time that a book o list changes
• A filter is expensive
• Watch a derive function is expensive
• Watch all books (aka $watch(,,true)) is expensive
37. NEW FEATURE - UPDATE BOOK
SHIT!
Solved in the next talk…
42. ORIGINAL MVC
SHIT!
It is not as beautiful and easy as it seems.
May be it worked in ’78, with just few Kb…
and low complexity, but now…
43. FB comments about MVC
ARCHITECTURES VARIATIONS
https://youtu.be/nYkdrAPrdcw?t=10m20s
44. FB comments about MVC
ARCHITECTURES VARIATIONS
“MVC works pretty well
for small applications…
but it doesn’t make room
for new features.”
“Flux is a single direction data flow,
that avoids all the arrows
going on all directions
what makes really
hard to understand the system.”
45. FB comments about MVC
ARCHITECTURES VARIATIONS
“Let’s see a real good example: FB chat”
“How we get to the point, so we were annoying our
users so much the just they wanted us to fix chat?”
“The problems here were:
• The code has no structure
• It was very imperative, that makes it fragile
• It loose a lot of the original intend behind it, its hard to tell what it tries [to do]
• Add more features only gets this code larger
• We had our most annoying chat bug happen over and over
• We were always fixing some pretty good edge case, the whole system was fragile
• …
• This code becomes more fragile with the time.
• No member of the team wanted to touch it, they wanted to jump to any other bug.”
53. the same flow
FROM MVC TO MVS
SERVICES
MODELS VIEWS
calls
updates
are $watched
54. MVS RULES (I)
SERVICES
MODELS VIEWS
calls
updates
are $watched
• Views can call to services
• Services manages models
• Models are watched by views through $watch
• Views cannot modify models but temporary copies
• Each services has its own models and do not modify other
services models
55. Key points are:
• Single direction flow
• Avoid imperative code
• Decoupled state from views
• Computed data consistency
(example: sciFiBooks = books.filter(isSciFi))
ANGULAR MVS
56. SOLUTION FOR SCI-FI-BOOKS
• Create a new model for SciFiBooks
• Add a method getSciFiBooks() to bookService
• Recompute the list each time that an update() method is called
60. SOLUTION FOR COMPUTED VALUES
MODELS
• Models launch update: ex $broadcast(‘booksChanged’)
for angular1
fire events
61. SOLUTION FOR COMPUTED VALUES
MODELS
• Models launch update: ex $broadcast(‘booksChanged’)
• Rules:
• Each model has its own event (1 <-> 1)
• We do not pass any information (ex: previous and new value)
• Change event is fired by the Service that manages the model
• Events can be debounced (multiple collapsed in one)
rules
fire events
62. booksService (update)
app.service(‘booksService’, (booksDictionary, $http, $broadcast) => {
…
this.update = (book) => {
var id = book.id;
this.books[id] = $http.put(‘/api/v1/books/’+id).then(response => {
var book = booksDictionary.getOrCreate(id);
book.takeData(response.data);
$broadcast(‘booksChanged’);
return book;
});
};
});
SOLUTION FOR SCI-FI-BOOKS
74. the same flow
MVS
SERVICES
MODELS VIEWS
calls
updates
are $watched
Dumb
Views
Smart
Views
BROWSER
uses listens
uses listens
Services
Remotes Storages
callscalls
State
Holders
State
Singletons
State
Dictionaries
CI
CI
CICICI
75. computed data flow
MVS
SERVICES
MODELS VIEWS
calls
updates
are $watched
Dumb
Views
Smart
Views
BROWSER
uses listens
uses listens
Services
Remotes Storages
callscalls
State
Holders
State
Singletons
State
Dictionaries
CI
CI
CICICI
76. computed data flow
MVS
SERVICES
MODELS VIEWS
calls
updates
are $watched
Dumb
Views
Smart
Views
BROWSER
uses listens
uses listens
Services
Remotes Storages
callscalls
State
Holders
State
Singletons
State
Dictionaries
CI
CI
CICICI
are listened
are listened
77. step by step
COMPUTED DATA FLOW
1. call
5. is fired
Books
Service
Books
Dictionary
Sci-Fi
Service
Sci-Fi
Dictionary
2. updates
3. fire $update
7. updates
8. fire $update
$UPDATE
EVENT MANAGER
4. fire $update
6. reads