当前位置:网站首页>Transfer the idea of "Zhongtai" to the code
Transfer the idea of "Zhongtai" to the code
2022-07-05 15:22:00 【Ardor-Zhang】
List of articles
hold ” Zhongtai “ The idea of moving to the code
Abstract : Different service providers correspond to different interfaces , How to avoid mixing judgment logic directly in the code ? How can I use it without caring about the interface provided by a service , What parameters are needed , What structure to respond to and what exception error to return ? This article is a good medicine , hold ” Zhongtai “ The idea of is introduced into the code , Solve this problem pointedly .
1 How to ~
First, let's take a look at a practical example , What problems have you encountered in the project .
When developing login registration function , There are two third-party services that users can choose at will —— Firebase and Supabase The way ( It can be understood as wechat and QQ land ), They provide the following capabilities :
- Email registration
- Firebase —— createUserWithEmailAndPassword
- Supabase —— signUp
- Mailbox validation
- Firebase —— sendEmailVerification
- Supabase —— emailForVerification
- Email login
- Firebase —— signInWithEmailAndPassword
- Supabase —— signIn
- Password reset
- Firebase —— sendPasswordResetEmail
- Supabase —— resetPasswordForEmail
- The user to log out
- Firebase —— signOut
- Supabase —— signOut
You can see , Different services have completely different interfaces , besides , The interfaces provided by different services also have the following differences :
- The required parameter formats are different
- The format of the returned response body is different
- The capture formats of exception errors are different
When used in a project , Because there is no logical unity , In the code A lot of judgment logic , It is used to realize that the bottom layer correctly uses the corresponding interface when users choose to use different services , Such as the following code :
// User registration logic
if( Services selected by users == Firebase) {
formatFirebaseParams() // Format registration parameters , Different services require different parameter formats
try {
response = createUserWithEmailAndPassword(...) // Firebase Registration logic
user = formatFirebaseResponse(response) // Format the return body , Different services have different return body formats
} catch (e) {
// Handling exceptions , Different services catch different exception error formats
if(e.code === ' The mailbox already exists ') {
throw ' The mailbox already exists '
} else if(){
} ....
...
}
} else {
formatSupabaseParams() // Format registration parameters
try {
response = registerByEmail(...) // Supabase Registration logic
user = formatSupabaseResponse(response) // Format the return body
} catch (e) {
// Handling exceptions
if(e.code === 1001) {
throw ' The mailbox already exists '
} else if(){
} ....
...
}
}
As you can see from above , Without unified treatment , these Logic is mixed directly, which is very chaotic in business , also , Predictably enough , With the increase of third-party services providing login , The logic here will be increased accordingly , This will bring huge costs to the development :
- Just add a new service type , Every place using services needs to add corresponding logic
- Just add a new place to use , You need to add the logic corresponding to all services
2. reflection ?
Is there a way to solve this problem ? It can realize the following features :
Interface for unified use
- Whether the user chooses Firebase still Supabase, The unified interface is called in the business code , Such as createUser(email, password), No longer differentiate Services .
- meanwhile , There is no need to format parameters by services when calling business code
Unified return body format
- Whether the user chooses Firebase still Supabase, The structure of the response body returned when used in the business code is consistent
Unified exception capture format
- Whether the user chooses Firebase still Supabase, For the same type of error , The errors returned during capture are consistent
3. Walk up !
How can we make different services provide the same set of interfaces ? It's not hard to think , Namely Zhongtai Thought , Realize a middle platform :
- foreign : Provide unified interface services to business parties , Of course, business parties can choose the underlying services they want to use at will
- internal : Put different underlying services together , Encapsulate their interfaces into consistent interfaces , Include :
- Format of parameters
- Interface encapsulation
- Unified capture of abnormal errors
Unified interface
Use abstract classes to unify exposed interfaces
export default abstract class AuthBase { abstract initialize(): void; abstract get currentUser(): User; abstract createUser(email: string, password: string): Promise<User>; abstract emailVerification(): Promise<void>; abstract loginIn(email: string, password: string): Promise<User>; abstract logOut(): Promise<void>; abstract passwordReset(email: string): Promise<void>; }
Encapsulate third-party services based on abstract classes
import { createUserWithEmailAndPassword } from "firebase/auth"; import { WeakPasswordAuthException, EmailAlreadyInUseAuthException, InvalidEmailAuthException, GenericAuthException, } from "../auth_exceptions"; // firebase To implement the base class export default class FirebaseAuthProvider implements AuthBase { auth: Auth; initialize(): void { ... } get currentUser(): User { ... } // Implement unified methods in base classes async createUser(email: string, password: string): Promise<User> { // If you need to format parameters , Do it here try { // call firebase Corresponding services const { user } = await createUserWithEmailAndPassword( this.auth, // firebae Required parameters email, password ); // Return the unified response body structure return { id: user.uid, email: user.email!, isEmailVerified: user.emailVerified, }; } catch (e) { switch (e.code) { // Capture the error , And throw a unified error response case "email-already-in-use": throw new EmailAlreadyInUseAuthException(); case "weak-password": throw new WeakPasswordAuthException(); case "invalid-email": throw new InvalidEmailAuthException(); default: throw new GenericAuthException(); } } } async emailVerification(): Promise<void> { ... } async loginIn(email: string, password: string): Promise<User> { ... } async logOut(): Promise<void> { ... } async passwordReset(email: string): Promise<void> { ... } }
import { User } from "../types"; import AuthBase from "../auth_base"; import { createClient, SupabaseClient, AuthUser } from "@supabase/supabase-js"; import { WeakPasswordAuthException, EmailAlreadyInUseAuthException, InvalidEmailAuthException, GenericAuthException, } from "../auth_exceptions"; // supabase To implement the base class export default class SupabaseAuthProvider implements AuthBase { supabase: SupabaseClient; initialize(): void { ... } get currentUser(): User { ... } // Implement unified methods in base classes async createUser(email: string, password: string): Promise<User> { try { const { user, error } = await this.supabase.auth.signIn({ email, // supa Required parameters password, }); if (user !== null) { // Return the unified response body structure return { id: user?.id, email: user.email!, isEmailVerified: !!user.new_email, }; } else { const status = error?.status; switch (status) { // Capture the error , And throw a unified error response case 1001: throw new EmailAlreadyInUseAuthException(); case 1002: throw new WeakPasswordAuthException(); case 1003: throw new InvalidEmailAuthException(); default: throw new GenericAuthException(); } } } catch (e) { throw new GenericAuthException(); } } async emailVerification(): Promise<void> { ... } async loginIn(email: string, password: string): Promise<User> { ... } async logOut(): Promise<void> { ... } async passwordReset(email: string): Promise<void> { ... } }
ad locum , Base class based , We have achieved
Encapsulate the corresponding interfaces of each service and provide a unified external interface
Unified return body format
Unified error capture
Aggregation of multiple services
import { User } from "./types"; import AuthBase from "./auth_base"; import FirebaseAuthProvider from "./provider/firebase_auth_provider"; import SupabaseAuthProvider from "./provider/supabase_auth_provider"; const providerMap = { firebase: new FirebaseAuthProvider(), supabase: new SupabaseAuthProvider(), } as const; export type ProviderEnum = keyof typeof providerMap; // Aggregate all services , Businesses do not need to be aware of the underlying services when using , Use a unified interface directly export default class AuthService implements AuthBase { constructor(private provider: AuthBase) { } static instance(provider: ProviderEnum) { return new AuthService(providerMap[provider]); } initialize(): void { this.provider.initialize(); } get currentUser(): User { return this.provider.currentUser; } createUser(email: string, password: string): Promise<User> { return this.provider.createUser(email, password); } emailVerification(): Promise<void> { return this.provider.emailVerification(); } loginIn(email: string, password: string): Promise<User> { return this.provider.loginIn(email, password); } logOut(): Promise<void> { return this.provider.logOut(); } passwordReset(email: string): Promise<void> { return this.provider.passwordReset(email); } }
Use
import AuthService from "./auth/auth_service"; import { WeakPasswordAuthException, EmailAlreadyInUseAuthException, InvalidEmailAuthException, } from "./auth/auth_exceptions"; // When a user chooses to use a third-party service , Just instantiate it here , All subsequent interfaces are consistent , There is no need to make any judgment const provider = AuthService.instance("firebase"); // No matter what service you use , When used, there are consistent interfaces 、 Parameters 、 Response body and error response async function handleRegister(email: string, password: string) { try { const user = await provider.createUser(email, password); // user use the same pattern } catch (e) { if (e instanceof EmailAlreadyInUseAuthException) { // Throw the corresponding error message } else if (e instanceof WeakPasswordAuthException) { // Throw the corresponding error message } else if (e instanceof InvalidEmailAuthException) { // Throw the corresponding error message } else { // Throw the corresponding error message } } }
When users choose a service , There is no need to care about the user's choice in the business code , There is no need to make any logical judgment , Just select the corresponding service when initializing the instance , All subsequent logics do not need to judge and handle different services in the business , Because it 's already :
- Unified interface
- Unified return body
- Unified exception error
4. Think …
In fact, such an idea is not difficult , But it's hard to see in the project , Students who are not developers cannot write , More should be not thinking in this direction , I didn't follow up conceptually .
Record such thoughts here , After that, as long as you encounter the same scene , Try to lean in this direction . I Believe , Such code is readable and maintainable , Will be much higher .
边栏推荐
- First PR notes
- Good article inventory
- JS bright blind your eyes date selector
- Common redis data types and application scenarios
- JS topic - console log()
- CPU design practice - Chapter 4 practical task 2 using blocking technology to solve conflicts caused by related problems
- Common MySQL interview questions
- Severlet learning foundation
- Long list optimized virtual scrolling
- CPU design practice - Chapter 4 practice task 3 use pre delivery technology to solve conflicts caused by related issues
猜你喜欢
随机推荐
Selection and use of bceloss, crossentropyloss, sigmoid, etc. in pytorch classification
Surpass palm! Peking University Master proposed diverse to comprehensively refresh the NLP reasoning ranking
mapper.xml文件中的注释
ICML 2022 | 探索语言模型的最佳架构和训练方法
Run faster with go: use golang to serve machine learning
CPU design practice - Chapter 4 practical task 2 using blocking technology to solve conflicts caused by related problems
Calculate weight and comprehensive score by R entropy weight method
CODING DevSecOps 助力金融企业跑出数字加速度
STM32+BH1750光敏传感器获取光照强度
计算中间件 Apache Linkis参数解读
Talk about your understanding of microservices (PHP interview theory question)
Redis distributed lock principle and its implementation with PHP (1)
sql server学习笔记
How to paste the contents copied by the computer into mobaxterm? How to copy and paste
The difference between abstract classes and interfaces in PHP (PHP interview theory question)
数据库学习——数据库安全性
CPU design practice - Chapter 4 practice task 3 use pre delivery technology to solve conflicts caused by related issues
Jmeter性能测试:ServerAgent资源监控
Database learning - Database Security
Common MySQL interview questions