# On Premises and Design patterns applied ###### tags: `Design Pattern` ## On premises ### What is on premises? On-premises refers to IT infrastructure hardware and software applications that are **hosted on-site (就地部屬)**. This contrasts with IT assets that are hosted by a public cloud platform or remote data center. Businesses have more control of on-premises IT assets by maintaining the performance, security and upkeep, as well as the physical location. ### Why is there still demand for on premises? **Disadvantages of cloud service: Privacy Issues** The availability of information can be breached when usingcloud computing. Your data can be accessed by unauthorizedentities or even modified. ### The reason of applying design patterns - Keep single code base for a product for both web app and on premise with the consideration for future update - Yet, APIs and request header token for the 2 scenario are different for identical purpose. Ex: creating a project through api - web app: stored in the cloud. Need CRUD - on premise: creating project stored in local SQLite db. Need CRUD - Encapsulate the logic (ex: which api to call) in one place, making rest of the code base (ex: where one of the api need to be called) remained unchanged - ETC (Easy to change) <br> ## Applied design patterns brief intro ### Strategy design pattern > Strategy is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable. > Enable the exact behavior of a system to be selected at run-time #### Life analogy <u>Making hot drink</u> - Making coffee - water preparation: Boil water 200ml, lower temperature to 90 degree - making drink: grind one spoon of coffee beans into power, slowing pour hot water in - Making black tea - water preparation: Boil water 500ml - making drink: put teabag into the bottle, pour the water in, remove teabag after 2 min :::success **Why applying this design pattern?** Both web app and on premise have identical method (ex: CRUD project) ::: #### Code example ```typescript= /** * The Context defines the interface of interest to clients. */ class Context { private strategy: Strategy; /** * Usually, the Context accepts a strategy through the constructor, but also * provides a setter to change it at runtime. */ constructor(strategy: Strategy) { this.strategy = strategy; } /** * Usually, the Context allows replacing a Strategy object at runtime. */ public setStrategy(strategy: Strategy) { this.strategy = strategy; } /** * The Context delegates some work to the Strategy object instead of * implementing multiple versions of the algorithm on its own. */ public doSomeBusinessLogic(): void { // ... console.log('Context: Sorting data using the strategy (not sure how it\'ll do it)'); const result = this.strategy.doAlgorithm(['a', 'b', 'c', 'd', 'e']); console.log(result.join(',')); // ... } } /** * The Strategy interface declares operations common to all supported versions * of some algorithm. * * The Context uses this interface to call the algorithm defined by Concrete * Strategies. */ interface Strategy { doAlgorithm(data: string[]): string[]; } /** * Concrete Strategies implement the algorithm while following the base Strategy * interface. The interface makes them interchangeable in the Context. */ class ConcreteStrategyA implements Strategy { public doAlgorithm(data: string[]): string[] { return data.sort(); } } class ConcreteStrategyB implements Strategy { public doAlgorithm(data: string[]): string[] { return data.reverse(); } } /** * The client code picks a concrete strategy and passes it to the context. The * client should be aware of the differences between strategies in order to make * the right choice. */ const context = new Context(new ConcreteStrategyA()); console.log('Client: Strategy is set to normal sorting.'); context.doSomeBusinessLogic(); console.log(''); console.log('Client: Strategy is set to reverse sorting.'); context.setStrategy(new ConcreteStrategyB()); context.doSomeBusinessLogic(); ``` log result ``` Client: Strategy is set to normal sorting. Context: Sorting data using the strategy (not sure how it'll do it) a,b,c,d,e Client: Strategy is set to reverse sorting. Context: Sorting data using the strategy (not sure how it'll do it) e,d,c,b,a ``` <br> ### Factory design pattern > Factory method is a creational design pattern which solves the problem of creating product objects without specifying their concrete classes. > A component responsible solely for the wholesale creation of objects #### Life analogy <u>Logistic management</u> <img src="https://refactoring.guru/images/patterns/content/factory-method/factory-method-en.png?id=cfa26f33dc8473e803fadae0d262100a"/> :::success **Why applying this design pattern?** We want to decide with set of CRUD api to use without passing params everytime ::: #### Code sample ```typescript /** * The Creator class declares the factory method that is supposed to return an * object of a Product class. The Creator's subclasses usually provide the * implementation of this method. */ abstract class Creator { /** * Note that the Creator may also provide some default implementation of the * factory method. */ public abstract factoryMethod(): Product; /** * Also note that, despite its name, the Creator's primary responsibility is * not creating products. Usually, it contains some core business logic that * relies on Product objects, returned by the factory method. Subclasses can * indirectly change that business logic by overriding the factory method * and returning a different type of product from it. */ public someOperation(): string { // Call the factory method to create a Product object. const product = this.factoryMethod(); // Now, use the product. return `Creator: The same creator's code has just worked with ${product.operation()}`; } } /** * Concrete Creators override the factory method in order to change the * resulting product's type. */ class ConcreteCreator1 extends Creator { /** * Note that the signature of the method still uses the abstract product * type, even though the concrete product is actually returned from the * method. This way the Creator can stay independent of concrete product * classes. */ public factoryMethod(): Product { return new ConcreteProduct1(); } } class ConcreteCreator2 extends Creator { public factoryMethod(): Product { return new ConcreteProduct2(); } } /** * The Product interface declares the operations that all concrete products must * implement. */ interface Product { operation(): string; } /** * Concrete Products provide various implementations of the Product interface. */ class ConcreteProduct1 implements Product { public operation(): string { return '{Result of the ConcreteProduct1}'; } } class ConcreteProduct2 implements Product { public operation(): string { return '{Result of the ConcreteProduct2}'; } } /** * The client code works with an instance of a concrete creator, albeit through * its base interface. As long as the client keeps working with the creator via * the base interface, you can pass it any creator's subclass. */ function clientCode(creator: Creator) { // ... console.log('Client: I\'m not aware of the creator\'s class, but it still works.'); console.log(creator.someOperation()); // ... } /** * The Application picks a creator's type depending on the configuration or * environment. */ console.log('App: Launched with the ConcreteCreator1.'); clientCode(new ConcreteCreator1()); console.log(''); console.log('App: Launched with the ConcreteCreator2.'); clientCode(new ConcreteCreator2()); ``` log result ``` App: Launched with the ConcreteCreator1. Client: I'm not aware of the creator's class, but it still works. Creator: The same creator's code has just worked with {Result of the ConcreteProduct1} App: Launched with the ConcreteCreator2. Client: I'm not aware of the creator's class, but it still works. Creator: The same creator's code has just worked with {Result of the ConcreteProduct2} ``` <br> ### Singleton design pattern > Singleton is a creational design pattern that lets you ensure that a class **has only one instance**, while providing a global access point to this instance. <img src="https://refactoring.guru/images/patterns/content/singleton/singleton.png?id=108a0b9b5ea5c4426e0afa4504491d6f"> :::success **Why applying this design pattern?** There's no need to create the object which contains identical method everytime ::: #### Code sample ```typescript /** * The Singleton class defines the `getInstance` method that lets clients access * the unique singleton instance. */ class Singleton { private static instance: Singleton; /** * The Singleton's constructor should always be private to prevent direct * construction calls with the `new` operator. */ private constructor() { } /** * The static method that controls the access to the singleton instance. * * This implementation let you subclass the Singleton class while keeping * just one instance of each subclass around. */ public static getInstance(): Singleton { if (!Singleton.instance) { Singleton.instance = new Singleton(); } return Singleton.instance; } /** * Finally, any singleton should define some business logic, which can be * executed on its instance. */ public someBusinessLogic() { // ... } } /** * The client code. */ function clientCode() { const s1 = Singleton.getInstance(); const s2 = Singleton.getInstance(); if (s1 === s2) { console.log('Singleton works, both variables contain the same instance.'); } else { console.log('Singleton failed, variables contain different instances.'); } } clientCode(); ``` log result ``` Singleton works, both variables contain the same instance. ``` <br> --- ## Apply design pattern to required on premises scenario ### module structure api > service > component/store ``` |---apis | |---studioApi | |---onPremStudioApi |---services | | | |--- userService | | |---index.ts (factory + singleton) | | |---userService.ts (strategy) | | |---onPremUserService.ts (strategy) | | | |--- studioServie | |---index.ts (factory + singleton) | |---studioService.ts (strategy) | |---onPremStudioService.ts (strategy) | |---store | |---view ``` ### ETC (Easy to change) discussion