DXäºæ¥é¨ã§LayerX ã¤ã³ãã¤ã¹ã®ã¯ã¼ã¯ããã¼æ©è½éçºãæ å½ãã¦ããæ¨æ¸ã¨ç³ãã¾ãã
ä»åã¯å æ¥ãªãªã¼ã¹ããã¯ã¼ã¯ããã¼æ©è½ã«ã¤ãã¦ã éçºéå§ãã2ã¶æã§ãªãªã¼ã¹ã«è³ãã¾ã§ã®æµãã¨ãæ¡ç¨ããGraphQLã§ã®ã¢ãã«è¨è¨ã«ã¤ãã¦ãç´¹ä»ãããã¾ãã
GraphQLãæ¡ç¨ããçµç·¯ã«ã¤ãã¦ã¯ããã¡ãã®ã¨ã³ããªã§ @mosa_siru ãç´¹ä»ãã¦ããã¾ãã®ã§å¾¡è¦§ãã ããã tech.layerx.co.jp
2021å¹´1æã®æ¬ãªãªã¼ã¹å¾ãã客æ§ããã®å£°ãå§åçã«å¤ãã£ããã¨ãããããããããï¼ã¨2æãã以ä¸ã®æé ã§è¨è¨ã»éçºãè¡ãã¾ããã
- ã¯ã¼ã¯ããã¼å ¨ä½ã«ã¤ãã¦æ©è½ã®æ´ãåºã
- æ©è½ãæºãããã¼ãã«è¨è¨
- GraphQLã®ã¹ãã¼ãå®ç¾©
- ã¹ãã¼ãå®ç¾©ããã³ã¼ãçæ
- å¦çãå®è£
ã¯ã¼ã¯ããã¼å ¨ä½ã«ã¤ãã¦æ©è½ã®æ´ãåºã
ç·æ¥äºæ
宣è¨ä¸ã ã£ããã¨ããããã¯ã¼ã¯ããã¼éçºãã¼ã å
ã§èªèãåããããã¨ãå°é£ãä¼´ãã¾ããã
ããã§æã
ãé¸æããã®ã¯miroãå©ç¨ãã¦ããããããå¿
è¦ã¨æãæ©è½ãä»ç®ã§ã©ãã©ãè²¼ã£ã¦ãããããã«å¯¾ãã¦æ©è½ã®åªå
度ä»ãã¨å¯¾å¿ææã決å®ãã¾ããã
æ©è½ãæºãããã¼ãã«è¨è¨
æ©è½ã®æ´ãåºããå®äºãã解å度ãä¸ãã£ãç¶æ
ã§ã次ã¯ãã¼ãã«è¨è¨ãè¡ãã¾ããã
ä¸è¿°ã®åªå
度ä»ãã¨å¯¾å¿ææã決å®ãã¾ãããããã¼ãã«è¨è¨ã¯åªå
度ã«é¢ããããå°æ¥å®è£
ããæ©è½ãè¦è¶ãã¦ãç´è¿åå¹´éããããã¯å°æ¥ä½ãæ©è½ã®ããã«ææ»ããçºçãããªããã¨ãæèãã¦è¨è¨ãã¦ãã¾ãã
GraphQLã®ã¹ãã¼ãå®ç¾©
å æ¥ã@yyoshiki ããç´¹ä»ããéããæã ã¯Schema Driven Developmentãæ¡ç¨ãã¦ãã¾ãã tech.layerx.co.jp
ã¯ã¼ã¯ããã¼æ©è½ã«ããã¦ã¯ãGraphQLã«ããã¹ãã¼ãå®ç¾©ãæ¡ç¨ãã¦ãã¾ãã
ããã¯ã¨ã³ãã¨ããã³ãã¨ã³ãã®å®ç¾©ãã¡ã¤ã«ãçæãã¡ã¤ã«ãªã©ã¯ä»¥ä¸ã®ãããªæ§æã«ãã¦ãã¾ãã
ããã¯ã¨ã³ã
graph âââ generated â  âââ generated.go # gqlgenã®çæãã¡ã¤ã«ãåºæ¬ããããªã âââ gqlgen.yml # è¨å®ãã¡ã¤ã« âââ model # model.graphqlsã®ã¹ãã¼ãå®ç¾©ããçæãããã¢ãã« â  âââ models_gen.go âââ model.graphqls # ã¹ãã¼ãå®ç¾© âââ model.resolvers.go # ã¢ãã«ã«embedããå®ç¾©ãããå ´åã¯å¦çã®å®è£ ãããã« âââ resolver.go # åæåå¦ç âââ schema.graphqls # Query(readç³»)ãMutation(æ´æ°ç³»)ã®å®ç¾© âââ schema.resolvers.go # QueryãMutationã®å¦çã®å®è£ ãããã«
ããã³ãã¨ã³ã
codegen.yml # è¨å®ãã¡ã¤ã« graphql âââ query.ts # GraphQLã¯ã¨ãªå®ç¾© types âââ generated.ts # graphql-codegenã®çæãã¡ã¤ã«
ããã§å®éã«ãããããã¦ã¼ã¶ã¼æ å ±ã®åå¾ã¨ä½æãä¾ã«ãã¦ä»¥ä¸ã«å®ç¾©ãã¦ã¿ã¾ãã
ããã¯ã¨ã³ã
model.graphqls
# ã¦ã¼ã¶ã¼ã¹ãã¼ãå®ç¾© type User { id: ID! name: String! email: String! }
schema.graphqls
type Query { user(userId: ID!): User! # ç¹å®ã¦ã¼ã¶ã¼æ å ±åå¾ } type Mutation { createUser(input: UserInput!): User! # ã¦ã¼ã¶ã¼ä½æ } # ã¦ã¼ã¶ã¼ä½ææã®ã¤ã³ãããç¨ã¹ãã¼ãå®ç¾© input UserInput { name: String! email: String! }
ããã³ãã¨ã³ã
query.ts
// schema.graphqlsã®Queryã®å 容ã«å¾ã£ã¦å®ç¾© export const GetUser = gql` query GetUser($userId: ID!) { user(userId: $userId) { id name email } } ` // schema.graphqlsã®Mutationã®å 容ã«å¾ã£ã¦å®ç¾© export const CreateUser = gql` mutation CreateUser($input: UserInput!) { updateUser(input: $input) { id name email } } `
ãããªæãã次ã¯ã³ã¼ãçæã§ãã
ã¹ãã¼ãå®ç¾©ããã³ã¼ãçæ
ä¸è¿°ã®ã¦ã¼ã¶ã¼å®ç¾©ã®ä¾ããã¨ã«ã³ã¼ãçæãã¦ã¿ã¾ãããã
ããã¯ã¨ã³ã
$ gqlgen
ããã«ããçæãããã³ã¼ãã¯ä»¥ä¸ã§ããgenerated.goã®ä¸èº«ã¯å®è£
ã®ä¸ã§æèä¸è¦ãªããå²æãã¾ãã
models_gen.go
type User struct { ID string `json:"id"` Name string `json:"name"` Email string `json:"email"` } type UserInput struct { Name string `json:"name"` Email string `json:"email"` }
ã¹ãã¼ãã«å®ç¾©ããã¢ãã«ãstructã§åºåããã¦ãã¾ãã
schema.resolvers.go
func (r *queryResolver) User(ctx context.Context, userID string) (*model.User, error) { panic(fmt.Errorf("not implemented")) } func (r *mutationResolver) CreateUser(ctx context.Context, input model.UserInput) (*model.User, error) { panic(fmt.Errorf("not implemented")) }
Queryã¨Mutationã«å®ç¾©ããã¡ã½ãããçæããã¾ãã
panic(fmt.Errorf("not implemented"))
å®è£
æã¯ãã®é¨åãæ¸ãæãã¦å¦çãå®è£
ãã¾ãã
ããã³ãã¨ã³ã
$ yarn graphql-codegen
以ä¸ã®ã³ã¼ããçæããã¾ãã
generated.ts
export type User = { __typename?: 'User'; id: Scalars['ID']; name: Scalars['String']; email: Scalars['String']; }; export type GetUserQueryVariables = Exact<{ id: Scalars['ID']; }>; export type UserInput = { name: Scalars['String']; email: Scalars['String']; }; export type CreateUserMutationVariables = Exact<{ input: UserInput; }>;
model.graphqlsãschema.graphqlsã®ã¹ãã¼ãå®ç¾©ããã¢ãã«ã¨ãQueryãMutationã®å¼æ°ã®ã¢ãã«ãçæããã¦ããã®ããããã¾ãã
å¦çãå®è£
ã¹ãã¼ãå®ç¾©ããã³ã¼ãã®çæã¾ã§è¡ãããã®ã§å¦çãå®è£ ãã¾ãã
ããã¯ã¨ã³ã
ããã¯ã¨ã³ãã®å¦çã¯ãã¸ãã¯ãservice層ãDBãªã©ã¸ã®ã¢ã¯ã»ã¹ã¯repository層ã§è¡ã£ã¦ãã¾ããservice層ãrepository層ã®å®è£
å
容ã¯å²æãã¾ãã
schema.resolvers.go
func (r *queryResolver) User(ctx context.Context, userID string) (*model.User, error) { user, err := r.UserService.Get(ctx, userID) if err != nil { return nil, err } return user, nil } func (r *mutationResolver) CreateUser(ctx context.Context, input model.UserInput) (*model.User, error) { user, err := r.UserService.Create(ctx, input.Name, input.Email) if err != nil { return nil, err } return user, nil }
ããã³ãã¨ã³ã
<script lang="ts"> import Vue from 'vue' import { GetUserQueryVariables, CreateUserMutationVariables, User, UserInput } from '~/types/generated' export default Vue.extend({ data() { return { user: {} as User, userForm: { name: '', email: '', } as UserInput, } }, methods: { // ç¹å®ã¦ã¼ã¶ã¼æ å ±åå¾ getUser(id) { const variables: GetUserQueryVariables = { id, } const res = await this.$apollo .query({ query: GetUser, variables, }) .catch(e => { console.error(e) return Promise.reject(e) }) this.user = res.data.user }, // ã¦ã¼ã¶ã¼ä½æ createUser() { const variables: CreateUserMutationVariables = { input: { name: this.userForm.name, email: this.userForm.email, }, } this.$apollo .mutate({ mutation: CreateUser, variables, }) .catch(e => { return Promise.reject(e) }) }, }, }) </script>
ããã¯ã¨ã³ãã®service層ãrepository層ãããã³ãã¨ã³ãã®UIã®å®è£ ã¯å¥éããªããã°ãªãã¾ãããã ä¸è¨ã®å®è£ ã§ããã³ãããããã¯ã¨ã³ãã¸ã®ã¦ã¼ã¶ã¼ä½æã¨åå¾ã®å¦çãå®è£ ã§ãã¾ããã
ãããã«
- ãã¼ã ã§ã¯ã¼ã¯ããã¼ã®ããããã®æ©è½ã®è§£å度ãä¸ã
- ãããå®ç¾ãããã¼ãã«è¨è¨ãè¡ã
- GraphQLã®ã¹ãã¼ãå®ç¾©ã«è½ã¨ãè¾¼ã¿
- ã³ã¼ãã®èªåçæãè¡ã
ãã¨ã§ãçæéã§ã®éçºãå®ç¾ã§ãã¾ããã
ã¾ãã¹ãã¼ãå®ç¾©ããã³ã¼ãçæã§ããã¯ã¨ã³ãã¨ããã³ãã¨ã³ãã®ã¢ãã«ãä½æãããã¨ã§ã
åã®å®ç¾©ãå³å¯ã«ãªããå®è£
æ¹æ³ã«ãã¬ããªããå質ã¨ã¹ãã¼ãã®ä¸¡æ¹ãæã«å
¥ãããã¨ãã§ããã¨èãã¦ãã¾ãã
ã¾ã ã¾ã 使ããã¦ã®GraphQLã§ã¯ããã¾ãããä»å¾ã使ãåãã¦ããè¯ããµã¼ãã¹éçºããã¦ãããã°ã¨æãã¾ãã
絶è³ã¨ã³ã¸ãã¢æ¡ç¨ä¸ã§ãã®ã§ãå°ãã§ãèå³ã®ããæ¹ã¯ä¸åº¦ã話ãããã¦ããã ããã°ã¨æãã¾ãï¼ herp.careers