当前位置:网站首页>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 .
边栏推荐
- mapper. Comments in XML files
- Optional parameters in the for loop
- Common PHP interview questions (1) (written PHP interview questions)
- Brief introduction of machine learning framework
- MySQL----函数
- Common redis data types and application scenarios
- I collect multiple Oracle tables at the same time. After collecting for a while, I will report that Oracle's OGA memory is exceeded. Have you encountered it?
- How to paste the contents copied by the computer into mobaxterm? How to copy and paste
- CODING DevSecOps 助力金融企业跑出数字加速度
- Ctfshow web entry command execution
猜你喜欢
随机推荐
Calculate weight and comprehensive score by R entropy weight method
Ionic Cordova project modification plug-in
What are the domestic formal futures company platforms in 2022? How about founder metaphase? Is it safe and reliable?
Bugku cyberpunk
Coding devsecops helps financial enterprises run out of digital acceleration
[recruitment position] infrastructure software developer
queryRunner. Query method
Reconnaissance des caractères easycr
Reasons and solutions for redis cache penetration and cache avalanche
做研究无人咨询、与学生不交心,UNC助理教授两年教职挣扎史
Common redis data types and application scenarios
Cartoon: programmers don't repair computers!
Hongmeng system -- Analysis from the perspective of business
sql server学习笔记
Misc Basic test method and knowledge points of CTF
华为哈勃化身硬科技IPO收割机
漫画:优秀的程序员具备哪些属性?
复现Thinkphp 2.x 任意代码执行漏洞
Talking about how dataset and dataloader call when loading data__ getitem__ () function
Run faster with go: use golang to serve machine learning



![1330: [example 8.3] minimum steps](/img/69/9cb13ac4f47979b498fa2254894ed1.gif)

![P6183 [USACO10MAR] The Rock Game S](/img/f4/d8c8763c27385d759d117b515fbf0f.png)




