Table of Contents
IntroductionLukas Ruebbelke discusses the outline and scope of the course. Exercises can be completed using the GitHub repository or through code examples on stackblitz.com.
Repo LogisticsLukas opens the Github repository and explains where to find the solutions to each exercise.
Why It's Called Enterprise PatternsLukas explains how the scope of the course relates to enterprise development. Reducing the complexity of code makes large-scale applications easier to build and maintain.
Managing State ComplexityLukas explains that managing complexity includes the proper handling of state, code volume, and flow of control. Any application with a shared mutable state can be difficult to test or share code across multiple components.
Dependency InjectionLukas explains that local, or micro, complexity is addressed at the component level. In order for component code to be testable, it should avoid hidden state and adhere to the single responsibility principle. The first step to fixing issues with hidden state is utilizing dependency injection.
Extract to MethodLukas uses the extract-to-method technique to address issues with flow control. When a method's logic becomes too complex or breaks the single responsibility principle, the code can be split into separate, reusable functions.
Separation of Concerns Q&ALukas answers a student question about how far a developer should go when splitting up logic and how to determine which methods to test.
Reducing Complexity ExerciseStudents are instructed to refactor the home service until all unit tests pass.
Reducing Complexity SolutionLukas live codes the solution to the Reducing Complexity exercise.
Object Oriented Programming
Feature ComplexityLukas describes the process of writing code as the continuous use of nouns, verbs, conditions, and iterators. Any programmatic task can be summarized in terms of these four concepts.
Object Modeling as NounsLukas explains the programming concepts around the object models within an application. Classes are the blueprint for models. Interfaces define a contract that classes must follow.
Defining & Typing ObjectsLukas creates two custom types and uses them to strongly type an Object. When an Object is strongly typed, IDEs are able to offer type-checking and code hinting.
Defining StateLukas demonstrates that modeling data objects in an application makes it easier to define and maintain state. Composing the state objects from existing data types makes the functionality in the component or application more clear.
Object Modeling Q&ALukas answers questions about why it was necessary to create a BaseEntity class, why the string type was used for for the id, and strategies for abstracting the data modeling outside the home component.
Object Modeling ExerciseStudents are instructed to create an object to represent a client project and implements an interface. An initialState object should also be created and implement a ProjectState interface.
Object Modeling SolutionLukas live codes the solution to the Object Modeling exercise.
Methods as VerbsLukas creates a ClientStore object. The methods within the ClientStore are the actions for that store.
Adding MethodsLukas refactors the ClientStore object to include the ClientsState object rather than the individual clients array and currentClient properties. The use of array notation is also explained in this segment.
Adding Methods ExerciseStudents are instructed to create a ProjectStore class with a constructor, state property, and the methods getState and select. The class should be instantiated with the initialState object and the select method should be called to retrieve the projects collection.
Adding Methods SolutionLukas live codes the solution to the Adding Methods exercise.
Decisions & Conditionals
Managing Flow ControlLukas explains that flow control in an application is managed through conditions. When a certain condition is true, the corresponding action is fired.
ReducersLukas demonstrates how reducers are used to manage state changes. A series of conditions are checked with a case statement to determine which action's method should be called. String-based action names should be replaced with constant variables to reduce code duplication.
Reducer ExerciseStudents are instructed to create a reducer function that accepts a state and an action parameter. A switch statement should evaluate the action type and call the appropriate method.
Reducer SolutionLukas live codes the solution to the Reducer exercise.
Collections & Iterators
Higher Order FunctionsLukas demonstrates why higher order functions like forEach more succinctly iterate over collections of data. Other useful higher order functions are filter, map, and reduce.
ImmutabilityLukas explains how to use immutable methods to create new items, update existing items, or delete an item within an array. Each of these immutable operations returns a new array and avoids a shared mutable state. This segment also discusses how to use the Object.freeze method.
Immutable CRUD Methods PracticeLukas demonstrates how to refactor the reducer methods using immutable operations. The concat method is be used for creating a new project. The map and assign methods are be used for updating projects. The filter method is used for deleting a project.
Immutability Q&ALukas answers questions about the Object.assign method and how to extract common CRUD operations for better reusability.
Immutable Store PracticeLukas implements an immutable store by adding a dispatch method that accepts an action and calls the reducer method with the state and the action.
Four Elements of Programming RecapLukas reviews how the four elements of programming have been used to build a light-weight state management module. The code is using the same architecture patterns as other state management libraries like Redux or NgRx.
Time Management in Applications
Observable StreamsLukas explains how to manage time, or asynchronicity, in applications. Observable streams encapsulate functionality, transport data asynchronously, and transform the data reliably.
Streaming Values Over TimeLukas compares observables and streaming values with promises. A promise is effective when managing a single asynchronous event. Observables coordinate multiple asynchronous events since streaming values are emitted over time.
Observables DemonstrationLukas demonstrates a basic observable example from the macro application. Data outputted from the stream is passed directly into the subscribe method. The data can be transformed before reaching the subscribe method by using the pipe method.
Observables ExerciseStudents are instructed to capture the searchControl output into a queryString. The data should also be mapped to uppercase letters and reversed.
Observables SolutionLukas live codes the solution to the Observables exercise.
Observables Q&ALukas answers questions about IDE theme configurations, promises vs. observables, and if data should be transformed on the server or client in an asynchronous call.
Preserving State & Merging StreamsLukas discusses how to preserve state within a stream. The scan method behaves like the reduce method by continually updating an initial value. The scan method can also receive a partially applied function for cases when multiple streams are being merged.
Mapping to FunctionsLukas explains in more detail how partially applied functions are passed into the stream and called at a later time. The mapTo method passes the property and value that should be applied to the position property once it enters the stream.
Sequencing Streams with switchMapLukas demonstrates how the switchMap method is used to switch to another stream. The takeUntil method allows the stream to receive data until a desired observable is emitted.
Communication with SubjectsLukas explains how communication messages are sent and received between subjects in an application. A subject's asObservable method returns a stream and any observer subscribed to the stream will receive updates when an event is emitted.
Observables Examples and Q&ALukas answers questions about currying, partially applied functions, and Redux vs. Observables.
Simulating User ActionsLukas discusses how user interactions can be simulated by manually dispatching actions. A properly decoupled system is not dependent on what triggers an action. It subscribes to a stream and waits for an action to be emitted.
Dynamic Actions & Real World BenefitsLukas demonstrates some real world examples of distributed complexity. Abstracting the state from an application allows two distinct instances of the application to share a synchronous stream of data through the use of web sockets.