├── .idea
├── misc.xml
├── modules.xml
├── servicetmpl1.iml
├── vcs.xml
├── workspace - 副本.xml
└── workspace.xml
├── .vscode
└── launch.json
├── LICENSE.txt
├── README.md
├── README.zh.md
├── app
├── app.go
├── config
│ ├── appConfig.go
│ ├── appConfigDev.yaml
│ ├── appConfigProd.yaml
│ └── configValidator.go
├── container
│ ├── container.go
│ ├── containerhelper
│ │ └── serviceTmplContainer.go
│ ├── dataservicefactory
│ │ ├── cacheDataServiceFacory.go
│ │ ├── dataServiceFactory.go
│ │ ├── userDataServiceFactoryWrapper.go
│ │ └── userdataservicefactory
│ │ │ ├── couchdbUserDataServiceFactory.go
│ │ │ ├── sqlUserDataServiceFactory.go
│ │ │ └── userDataServiceFactory.go
│ ├── datastorefactory
│ │ ├── cacheGrpcFactory.go
│ │ ├── couchdbFactory.go
│ │ ├── datastoreFactory.go
│ │ └── sqlFactory.go
│ ├── servicecontainer
│ │ └── serviceContainer.go
│ └── usecasefactory
│ │ ├── listUserFactory.go
│ │ ├── registrationFactory.go
│ │ ├── registrationTxFactory.go
│ │ ├── useCaseFactory.go
│ │ └── useCaseHelper.go
└── logger
│ └── logger.go
├── applicationservice
├── cacheclient
│ ├── cacheClient.go
│ └── generatedclient
│ │ ├── cacheJin.pb.go
│ │ └── doc.go
├── dataservice
│ ├── dataService.go
│ └── userdata
│ │ ├── couchdb
│ │ └── userDataCouchdb.go
│ │ └── sqldb
│ │ └── userDataSql.go
├── doc.go
├── paymentclient
│ └── paymentClient.go
└── userclient
│ ├── generatedclient
│ ├── usergrpc.pb.go
│ └── usergrpc.proto
│ └── userGrpc.go
├── cmd
├── grpcclient
│ └── grpcClientMain.go
├── grpcserver
│ └── grpcServerMain.go
└── main.go
├── domain
├── model
│ ├── cache.go
│ ├── course.go
│ └── user.go
└── usecase
│ ├── listuser
│ └── listUser.go
│ ├── registration
│ ├── registrationHelper.go
│ ├── registraton.go
│ └── registratonTx.go
│ └── useCase.go
├── go.mod
├── go.sum
├── script
└── user.sql
└── tool
├── doc.go
└── timea
└── timea.go
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/servicetmpl1.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace - 副本.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | jfeng
135 | AppConfig
136 | servicetmpl
137 | Gdbc
138 | userDataConfig
139 | registrationTx
140 | TxDataInterface
141 | logger2
142 | logger
143 | 2
144 | SQLConfig
145 | gdbc.
146 | servicetmpl1/tool/gdbc
147 | servicetmpl1/tool/txdataservice
148 | gdbc
149 | gtransaction
150 | initGdbc
151 | gtransaction/config
152 | logconfig
153 | EnableTx
154 | GetRegistrationUseCase
155 |
156 |
157 | servicetmpl1
158 | logger
159 | gtransation/gdbc
160 | gtransation/txdataservice
161 |
162 |
163 | D:\code\src\github.com\jfeng45\servicetmpl1
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 | true
433 | direct
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
821 |
822 |
823 |
824 |
825 |
826 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 | jfeng
134 | AppConfig
135 | servicetmpl
136 | Gdbc
137 | userDataConfig
138 | registrationTx
139 | TxDataInterface
140 | logger2
141 | logger
142 | 2
143 | SQLConfig
144 | gdbc.
145 | servicetmpl1/tool/gdbc
146 | servicetmpl1/tool/txdataservice
147 | gdbc
148 | gtransaction
149 | initGdbc
150 | gtransaction/config
151 | logconfig
152 | EnableTx
153 | GetRegistrationUseCase
154 |
155 |
156 | servicetmpl1
157 | logger
158 | gtransation/gdbc
159 | gtransation/txdataservice
160 |
161 |
162 | D:\code\src\github.com\jfeng45\servicetmpl1
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 | true
440 | direct
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 |
535 |
536 |
537 |
538 |
539 |
540 |
541 |
542 |
543 |
544 |
545 |
546 |
547 |
548 |
549 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 |
568 |
569 |
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
632 |
633 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
715 |
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 |
724 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
736 |
737 |
738 |
739 |
740 |
741 |
742 |
743 |
744 |
745 |
746 |
747 |
748 |
749 |
750 |
751 |
752 |
753 |
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
772 |
773 |
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 |
783 |
784 |
785 |
786 |
787 |
788 |
789 |
790 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
808 |
809 |
810 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
820 |
821 |
822 |
823 |
824 |
825 |
826 |
827 |
828 |
829 |
830 |
831 |
832 |
833 |
834 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "Launch",
9 | "type": "go",
10 | "request": "launch",
11 | "mode": "auto",
12 | "program": "${workspaceFolder}/cmd/grpcserver/grpcServerMain.go",
13 | "env": {},
14 | "args": []
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 Jin Feng
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # A Self-Evolved Microservice Framework in Go
2 |
3 | Other language:
4 | ### **[中文](README.zh.md)**
5 |
6 | This is a major upgrade version of [jfeng45/servicetmpl](https://github.com/jfeng45/servicetmpl).
7 |
8 | The followings are a series of articles to explain the different areas of the application design:
9 |
10 | + [Go Microservice with Clean Architecture-A Major Upgrade](
11 | https://medium.com/@jfeng45/go-microservice-with-clean-architecture-a-major-upgrade-34a4cedb0b06)
12 | + [A Self-Evolved Microservice Framework in Go](https://medium.com/@jfeng45/a-self-evolved-microservice-framework-in-go-d9bf87c10ab0)
13 | + [A Non-Intrusive Transaction Management Lib in Go — How to Use It](https://medium.com/swlh/a-non-intrusive-transaction-management-lib-in-go-how-to-use-it-a3e751cc1dd4)
14 | + [A Non-Intrusive Transaction Management Lib in Go — How it Works?](https://medium.com/nerd-for-tech/a-non-intrusive-transaction-management-lib-in-go-how-it-works-51d4b2ede8af)
15 |
16 |
17 | ## Getting Started
18 |
19 | ### Installation and Setting Up
20 |
21 | Don't need to finish all steps in this section up-front to get the code up running. The simplest way is to get the code from github and run it and come back to install the part when there is a real need. However, it will encounter an error when accesses the database. So, I'd recommend you install at least one database ( MySQL is better), then most of the code will work.
22 |
23 | #### Download Code
24 |
25 | ```
26 | go get github.com/jfeng45/servicetmpl1
27 | ```
28 |
29 | #### Set Up MySQL
30 |
31 | There are two database implementations, MySQL and CouchDB, but most functions are implemented in MySQL. You'd better install at least one of them.
32 | ```
33 | Install MySQL
34 | run SQL script in script folder to create database and table
35 | ```
36 | #### Install CouchDB
37 |
38 | The code works fine without it. CouchDB is created to show the feature of switching database by changing configuration.
39 |
40 | Installation on [Windows](https://docs.couchdb.org/en/2.2.0/install/windows.html)
41 |
42 | Installation on [Linux](https://docs.couchdb.org/en/2.2.0/install/unix.html)
43 |
44 | Installation on [Mac](https://docs.couchdb.org/en/2.2.0/install/mac.html)
45 |
46 | CouchDB [Example](https://github.com/go-kivik/kivik/wiki/Usage-Examples)
47 |
48 | #### Set up CouchDB
49 |
50 | ```
51 | Access "Fauxton" through browser: http://localhost:5984/_utils/# (login with: admin/admin).
52 | Create new database "service_config" in "Fauxton".
53 | Add the following document to the database ( "_id" and "_rev" are generated by database, no need to change it):
54 | {
55 | "_id": "80a9134c7dfa53f67f6be214e1000fa7",
56 | "_rev": "4-f45fb8bdd454a71e6ae88bdeea8a0b4c",
57 | "uid": 10,
58 | "username": "Tony",
59 | "department": "IT",
60 | "created": "2018-02-17T15:04:05-03:00"
61 | }
62 | ```
63 | #### Install Cache Service (Another Microservice)
64 |
65 | Without it, calling another Microservice piece won't work, the rest of application works fine. Please follow instructions in [reservegrpc](https://github.com/jfeng45/reservegrpc) to set up the service.
66 |
67 | ### Start Application
68 |
69 | #### Start MySQL Server
70 | ```
71 | cd [MySQLroot]/bin
72 | mysqld
73 | ```
74 |
75 | #### Start CouchDB Server
76 | ```
77 | It should already have been started
78 | ```
79 | #### Start Cache Service
80 |
81 | Please follow instructions in [reservegrpc](https://github.com/jfeng45/reservegrpc) to start the server.
82 |
83 | #### Run main
84 |
85 | ##### Run as a local application
86 | In "main()" function of "main.go", there are two functions "testMySql()" and "testCouchDB()".
87 | "testMySql()" reads configurations from "configs/appConifgDev.yaml" and accesses MySQL. "testCouchDB()" reads from "configs/appConifgProd.yaml" and access CouchDB.
88 | There are multiple functions in "testMySql()", you can focus on testing one each time by commenting out others.
89 | ```
90 | cd [rootOfProject]/cmd
91 | go run main.go
92 | ```
93 | ##### Run as a gRPC Microservice application
94 |
95 | Start gRPC Server
96 | ```
97 | cd [rootOfProject]/cmd/grpcserver
98 | go run grpcServerMain.go
99 | ```
100 | Start gRPC Client
101 | ```
102 | cd [rootOfProject]/cmd/grpcclient
103 | go run grpcClientMain.go
104 | ```
105 |
106 | ## License
107 |
108 | [MIT](LICENSE.txt) License
109 |
110 |
111 |
--------------------------------------------------------------------------------
/README.zh.md:
--------------------------------------------------------------------------------
1 | ## 一个能自我进化的Go微服务框架
2 |
3 | 其他语言:
4 |
5 | ### **[English](README.md)**
6 |
7 | 这是一个Go微服务框架。它是对[jfeng45/servicetmpl](https://github.com/jfeng45/servicetmpl1)的一个重大升级版。
8 |
9 | 下面是描述它的系列文章:
10 |
11 | + [一个能自我进化的Go微服务框架](https://blog.csdn.net/weixin_38748858/article/details/106996260)
12 |
13 | + [一个非侵入的Go事务管理库--怎样使用](https://blog.csdn.net/weixin_38748858/article/details/106885990)
14 |
15 | + [一个非侵入的Go事务管理库--工作原理](https://blog.csdn.net/weixin_38748858/article/details/106886184)
16 |
17 | + [清晰架构(Clean Architecture)的Go微服务--重大升级](https://blog.csdn.net/weixin_38748858/article/details/107565358)
18 |
19 | ## 运行
20 |
21 | ### 安装和设置
22 |
23 | 不需要完成本节中的所有步骤以使代码运行。 最简单的方法是从github获取代码并运行它,然后在真正需要某些部件时再返回安装。 但是,访问数据库时会遇到错误。
24 | 所以,我建议你至少安装一个数据库(MySQL更好),然后大部分代码就都可以运行了。
25 |
26 | #### 下载程序
27 |
28 | ```
29 | go get github.com/jfeng45/servicetmpl1
30 | ```
31 |
32 | #### 设置MySQL
33 |
34 | 有两个数据库实现,MySQL和CouchDB,但大多数函数都是在MySQL中实现的。 你最好安装至少其中一个。
35 |
36 | ```
37 | 安装MySQL
38 | 在script文件夹中运行SQL脚本以创建数据库和表
39 | ```
40 | #### 安装CouchDB
41 |
42 | 没有它,代码工作正常。创建CouchDB用来完成切换数据库的功能(通过更改配置)。
43 |
44 | 安装[Windows](https://docs.couchdb.org/en/2.2.0/install/windows.html)
45 |
46 | 安装[Linux](https://docs.couchdb.org/en/2.2.0/install/unix.html)
47 |
48 | 安装[Mac](https://docs.couchdb.org/en/2.2.0/install/mac.html)
49 |
50 | CouchDB[Example](https://github.com/go-kivik/kivik/wiki/Usage-Examples)
51 |
52 | #### 设置CouchDB
53 |
54 | ```
55 | 通过浏览器访问“Fauxton”:http://localhost:5984/_utils/#(使用:admin/admin登录)。
56 | 在“Fauxton”中创建新数据库“service_config”。
57 | 将以下文档添加到数据库(“_id”和“_rev”由数据库生成,无需更改):
58 | {
59 | "_id": "80a9134c7dfa53f67f6be214e1000fa7",
60 | "_rev": "4-f45fb8bdd454a71e6ae88bdeea8a0b4c",
61 | "uid": 10,
62 | "username": "Tony",
63 | "department": "IT",
64 | "created": "2018-02-17T15:04:05-03:00"
65 | }
66 | ```
67 | #### 安装缓存服务(另一个微服务)
68 |
69 | 没有它,调用另一个微服务部分将无法正常工作,其余部分工作正常。请按照[reservegrpc](https://github.com/jfeng45/reservegrpc)中的说明设置服务。
70 |
71 | ### 启动应用程序
72 |
73 | #### 启动MySQL
74 | ```
75 | cd [MySQLroot]/bin
76 | mysqld
77 | ```
78 |
79 | #### 启动CouchDB
80 | ```
81 | 它应该已经启动了
82 | ```
83 | #### 启动缓存服务
84 |
85 | 请按照[reservegrpc](https://github.com/jfeng45/reservegrpc)中的说明启动服务器。
86 |
87 | #### 运行main
88 |
89 | ##### 作为本地应用程序运行
90 |
91 | 在“main.go”的“main()”函数中,有两个函数“testMySql()”和“testCouchDB()”。
92 | “testMySql()”从“configs/appConifgDev.yaml”读取配置并访问MySQL。 “testCouchDB()”从“configs/appConifgProd.yaml”读取配置并访问CouchDB。
93 | “testMySql()”中有多个函数,你可以通过注释掉其他函数来单独测试一个函数。
94 |
95 | ```
96 | cd [rootOfProject]/cmd
97 | go run main.go
98 | ```
99 | ##### 作为gRPC微服务应用程序运行
100 |
101 | 启动gRPC服务器
102 | ```
103 | cd [rootOfProject]/cmd/grpcserver
104 | go run grpcServerMain.go
105 | ```
106 | 启动gRPC客户端
107 | ```
108 | cd [rootOfProject]/cmd/grpcclient
109 | go run grpcClientMain.go
110 | ```
111 |
112 | ### 授权
113 |
114 | [MIT](LICENSE.txt) 授权
115 |
116 |
117 |
--------------------------------------------------------------------------------
/app/app.go:
--------------------------------------------------------------------------------
1 | package app
2 |
3 | import (
4 | logConfig "github.com/jfeng45/glogger/config"
5 | logFactory "github.com/jfeng45/glogger/factory"
6 | "github.com/jfeng45/servicetmpl1/app/config"
7 | "github.com/jfeng45/servicetmpl1/app/container"
8 | "github.com/jfeng45/servicetmpl1/app/container/servicecontainer"
9 | "github.com/jfeng45/servicetmpl1/app/logger"
10 | "github.com/pkg/errors"
11 | )
12 |
13 | // InitApp loads the application configurations from a file and saved it in appConfig and initialize the logger
14 | // The appConfig is cached in container, so it only loads the configuration file once.
15 | // InitApp only needs to be called once. If the configuration changes, you can call it again to reinitialize the app.
16 | func InitApp(filename...string) (container.Container, error) {
17 | config, err := config.BuildConfig(filename...)
18 | if err != nil {
19 | return nil, errors.Wrap(err, "BuildConfig")
20 | }
21 | err = initLogger(&config.LogConfig)
22 | if err != nil {
23 | return nil, err
24 | }
25 | return initContainer(config)
26 | }
27 |
28 | func initLogger (lc *logConfig.Logging) error{
29 | log, err := logFactory.Build(lc)
30 | if err != nil {
31 | return errors.Wrap(err, "loadLogger")
32 | }
33 | logger.SetLogger(log)
34 | return nil
35 | }
36 |
37 | func initContainer(config *config.AppConfig) (container.Container, error) {
38 | factoryMap := make(map[string]interface{})
39 | c := servicecontainer.ServiceContainer{factoryMap,config}
40 | //gdbc, err :=initGdbc(&c.AppConfig.SQLConfig)
41 | //if err != nil {
42 | // return nil,err
43 | //}
44 | //key := config.SQLConfig.Code
45 | //c.Put(key, gdbc)
46 | return &c, nil
47 | }
48 |
--------------------------------------------------------------------------------
/app/config/appConfig.go:
--------------------------------------------------------------------------------
1 | // Package config reasd configurations from a YAML file and load them into a AppConfig type to save the configuration
2 | // information for the application.
3 | // Configuration for different environment can be saved in files with different suffix, for example [Dev], [Prod]
4 | package config
5 |
6 | import (
7 | "fmt"
8 | logConfig "github.com/jfeng45/glogger/config"
9 | "github.com/pkg/errors"
10 | "gopkg.in/yaml.v2"
11 | "io/ioutil"
12 | )
13 |
14 | // AppConfig represents the application config
15 | type AppConfig struct {
16 | SQLConfig DataStoreConfig `yaml:"sqlConfig"`
17 | SQLConfigTx DataStoreConfig `yaml:"sqlConfigTx"`
18 | CouchdbConfig DataStoreConfig `yaml:"couchdbConfig"`
19 | CacheGrpcConfig DataStoreConfig `yaml:"cacheGrpcConfig"`
20 | UserGrpcConfig DataStoreConfig `yaml:"userGrpcConfig"`
21 | ZapConfig logConfig.Logging `yaml:"zapConfig"`
22 | LorusConfig logConfig.Logging `yaml:"logrusConfig"`
23 | LogConfig logConfig.Logging `yaml:"logConfig"`
24 | UseCaseConfig UseCaseConfig `yaml:"useCaseConfig"`
25 | }
26 |
27 | // UseCaseConfig represents different use cases
28 | type UseCaseConfig struct {
29 | Registration RegistrationConfig `yaml:"registration"`
30 | RegistrationTx RegistrationTxConfig `yaml:"registrationTx"`
31 | ListUser ListUserConfig `yaml:"listUser"`
32 | }
33 |
34 | // RegistrationConfig represents registration use case
35 | type RegistrationConfig struct {
36 | Code string `yaml:"code"`
37 | UserDataConfig DataConfig `yaml:"userDataConfig"`
38 | }
39 |
40 | // RegistrationConfigTx represents registration use cases that support transaction
41 | type RegistrationTxConfig struct {
42 | Code string `yaml:"code"`
43 | UserDataConfig DataConfig `yaml:"userDataConfig"`
44 | }
45 |
46 | // ListUserConfig represents list user use case
47 | type ListUserConfig struct {
48 | Code string `yaml:"code"`
49 | UserDataConfig DataConfig `yaml:"userDataConfig"`
50 | CacheDataConfig DataConfig `yaml:"cacheDataConfig"`
51 | }
52 |
53 | // DataConfig represents data service
54 | type DataConfig struct {
55 | Code string `yaml:"code"`
56 | DataStoreConfig DataStoreConfig `yaml:"dataStoreConfig"`
57 | }
58 |
59 | // DataConfig represents handlers for data store. It can be a database or a gRPC connection
60 | type DataStoreConfig struct {
61 | Code string `yaml:"code"`
62 | // Only database has a driver name, for grpc it is "tcp" ( network) for server
63 | DriverName string `yaml:"driverName"`
64 | // For database, this is datasource name; for grpc, it is target url
65 | UrlAddress string `yaml:"urlAddress"`
66 | // Only some databases need this database name
67 | DbName string `yaml:"dbName"`
68 | // To indicate whether support transaction or not. "true" means supporting transaction
69 | Tx bool `yaml:"tx"`
70 | }
71 |
72 | // LogConfig represents logger handler
73 | // Logger has many parameters can be set or changed. Currently, only three are listed here. Can add more into it to
74 | // fits your needs.
75 | type LogConfig struct {
76 | // log library name
77 | Code string `yaml:"code"`
78 | // log level
79 | Level string `yaml:"level"`
80 | // show caller in log message
81 | EnableCaller bool `yaml:"enableCaller"`
82 | }
83 |
84 | // BuildConfig build the AppConfig
85 | // if the filaname is not empty, then it reads the file of the filename (in the same folder) and put it into the AppConfig
86 | func BuildConfig(filename ...string) (*AppConfig, error) {
87 | if len(filename) == 1 {
88 | return buildConfigFromFile(filename[0])
89 | } else {
90 | return BuildConfigWithoutFile()
91 | }
92 | }
93 |
94 | // BuildConfigWithoutFile create AppConfig with adhoc value
95 | func BuildConfigWithoutFile() (*AppConfig, error) {
96 | return nil, nil
97 | }
98 |
99 | // buildConfigFromFile reads the file of the filename (in the same folder) and put it into the AppConfig
100 | func buildConfigFromFile(filename string) (*AppConfig, error) {
101 |
102 | var ac AppConfig
103 | file, err := ioutil.ReadFile(filename)
104 | if err != nil {
105 | return nil, errors.Wrap(err, "read error")
106 | }
107 | err = yaml.Unmarshal(file, &ac)
108 |
109 | if err != nil {
110 | return nil, errors.Wrap(err, "unmarshal")
111 | }
112 | err = validateConfig(ac)
113 | if err != nil {
114 | return nil, errors.Wrap(err, "validate config")
115 | }
116 | fmt.Println("appConfig:", ac)
117 | return &ac, nil
118 | }
119 |
120 |
121 |
--------------------------------------------------------------------------------
/app/config/appConfigDev.yaml:
--------------------------------------------------------------------------------
1 | sqlConfig: &sqlConfig
2 | code: sqldb
3 | driverName: mysql
4 | urlAddress: "root:@tcp(localhost:4333)/service_config?charset=utf8"
5 | dbName:
6 | tx: false
7 | sqlConfigTx: &sqlConfigTx
8 | code: sqldb
9 | driverName: mysql
10 | urlAddress: "root:@tcp(localhost:4333)/service_config?charset=utf8"
11 | dbName:
12 | tx: true
13 | couchdbConfig: &couchdbConfig
14 | code: couch
15 | driverName: couch
16 | urlAddress: http://admin:admin@localhost:5984
17 | dbName: service_config
18 | tx: false
19 | cacheGrpcConfig: &cacheGrpcConfig
20 | code: cacheGrpc
21 | driverName: tcp
22 | urlAddress: localhost:5051
23 | userGrpcConfig: &userGrpcConfig
24 | code: userGrpc
25 | driverName: tcp
26 | urlAddress: localhost:5052
27 | zapConfig: &zapConfig
28 | code: zap
29 | level: debug
30 | enableCaller: true
31 | logrusConfig: &logrusConfig
32 | code: logrus
33 | level: debug
34 | enableCaller: false
35 | logConfig: *zapConfig
36 | useCaseConfig:
37 | registration:
38 | code: registration
39 | userDataConfig: &userDataConfig
40 | code: userData
41 | dataStoreConfig: *sqlConfig
42 | listUser:
43 | code: listUser
44 | userDataConfig: *userDataConfig
45 | cacheDataConfig: &cacheDataConfig
46 | code: cacheData
47 | dataStoreConfig: *cacheGrpcConfig
48 | registrationTx:
49 | code: registrationTx
50 | userDataConfig: &userDataConfigTx
51 | code: userData
52 | dataStoreConfig: *sqlConfigTx
53 |
54 |
55 |
--------------------------------------------------------------------------------
/app/config/appConfigProd.yaml:
--------------------------------------------------------------------------------
1 | sqlConfig: &sqlConfig
2 | code: sqldb
3 | driverName: mysql
4 | urlAddress: "root:@tcp(localhost:4333)/service_config?charset=utf8"
5 | dbName:
6 | tx: false
7 | sqlConfigTx: &sqlConfigTx
8 | code: sqldb
9 | driverName: mysql
10 | urlAddress: "root:@tcp(localhost:4333)/service_config?charset=utf8"
11 | dbName:
12 | tx: true
13 | couchdbConfig: &couchdbConfig
14 | code: couch
15 | driverName: couch
16 | urlAddress: http://admin:admin@localhost:5984
17 | dbName: service_config
18 | tx: false
19 | cacheGrpcConfig: &cacheGrpcConfig
20 | code: cacheGrpc
21 | driverName: cache
22 | urlAddress: localhost:5051
23 | userGrpcConfig: &userGrpcConfig
24 | code: userGrpc
25 | driverName: tcp
26 | urlAddress: localhost:5052
27 | zapConfig: &zapConfig
28 | code: zap
29 | level: debug
30 | enableCaller: true
31 | logrusConfig: &logrusConfig
32 | code: logrus
33 | level: debug
34 | enableCaller: false
35 | logConfig: *zapConfig
36 | useCaseConfig:
37 | registration:
38 | code: registration
39 | userDataConfig: &userDataConfig
40 | code: userData
41 | dataStoreConfig: *couchdbConfig
42 | listUser:
43 | code: listUser
44 | userDataConfig: *userDataConfig
45 | cacheDataConfig: &cacheDataConfig
46 | code: cacheData
47 | dataStoreConfig: *cacheGrpcConfig
48 | registrationTx:
49 | code: registrationTx
50 | userDataConfig:
51 | code: userData
52 | dataStoreConfig: *couchdbConfig
--------------------------------------------------------------------------------
/app/config/configValidator.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/pkg/errors"
5 | )
6 |
7 | // database code. Need to map to the database code (DataStoreConfig) in the configuration yaml file.
8 | const (
9 | SQLDB string = "sqldb"
10 | COUCHDB string = "couch"
11 | CACHE_GRPC string = "cacheGrpc"
12 | USER_GRPC string = "userGrpc"
13 | )
14 |
15 | // constant for logger code, it needs to match log code (logConfig)in configuration
16 | const (
17 | LOGRUS string = "logrus"
18 | ZAP string = "zap"
19 | )
20 |
21 | // use case code. Need to map to the use case code (UseCaseConfig) in the configuration yaml file.
22 | // Client app use those to retrieve use case from the container
23 | const (
24 | REGISTRATION string = "registration"
25 | REGISTRATION_TX string = "registrationTx"
26 | LIST_USER string = "listUser"
27 | )
28 |
29 | // data service code. Need to map to the data service code (DataConfig) in the configuration yaml file.
30 | const (
31 | USER_DATA string = "userData"
32 | CACHE_DATA string = "cacheData"
33 | TX_DATA string = "txData"
34 | //COURSE_DATA string = "courseData"
35 | )
36 |
37 | func validateConfig(appConfig AppConfig) error {
38 | err := validateDataStore(appConfig)
39 | if err != nil {
40 | return errors.Wrap(err, "")
41 | }
42 | err = validateLogger(appConfig)
43 | if err != nil {
44 | return errors.Wrap(err, "")
45 | }
46 | useCase := appConfig.UseCaseConfig
47 | err = validateUseCase(useCase)
48 | if err != nil {
49 | return errors.Wrap(err, "")
50 | }
51 | return nil
52 | }
53 |
54 | func validateLogger(appConfig AppConfig) error {
55 | zc := appConfig.ZapConfig
56 | key := zc.Code
57 | zcMsg := " in validateLogger doesn't match key = "
58 | if ZAP != key {
59 | errMsg := ZAP + zcMsg + key
60 | return errors.New(errMsg)
61 | }
62 | lc := appConfig.LorusConfig
63 | key = lc.Code
64 | if LOGRUS != lc.Code {
65 | errMsg := LOGRUS + zcMsg + key
66 | return errors.New(errMsg)
67 | }
68 | return nil
69 | }
70 |
71 | func validateDataStore(appConfig AppConfig) error {
72 | sc := appConfig.SQLConfig
73 | key := sc.Code
74 | scMsg := " in validateDataStore doesn't match key = "
75 | if SQLDB != key {
76 | errMsg := SQLDB + scMsg + key
77 | return errors.New(errMsg)
78 | }
79 | cc := appConfig.CouchdbConfig
80 | key = cc.Code
81 | if COUCHDB != key {
82 | errMsg := COUCHDB + scMsg + key
83 | return errors.New(errMsg)
84 | }
85 | cgc := appConfig.CacheGrpcConfig
86 | key = cgc.Code
87 | if CACHE_GRPC != key {
88 | errMsg := CACHE_GRPC + scMsg + key
89 | return errors.New(errMsg)
90 | }
91 |
92 | ugc := appConfig.UserGrpcConfig
93 | key = ugc.Code
94 | if USER_GRPC != key {
95 | errMsg := USER_GRPC + scMsg + key
96 | return errors.New(errMsg)
97 | }
98 |
99 | return nil
100 | }
101 |
102 | func validateUseCase(useCase UseCaseConfig) error {
103 | err := validateRegistration(useCase)
104 | if err != nil {
105 | return errors.Wrap(err, "")
106 | }
107 | err = validateRegistrationTx(useCase)
108 | if err != nil {
109 | return errors.Wrap(err, "")
110 | }
111 | err = validateListUser(useCase)
112 | if err != nil {
113 | return errors.Wrap(err, "")
114 | }
115 | return nil
116 | }
117 |
118 | func validateRegistration(useCaseConfig UseCaseConfig) error {
119 | rc := useCaseConfig.Registration
120 | key := rc.Code
121 | rcMsg := " in validateRegistration doesn't match key = "
122 | if REGISTRATION != key {
123 | errMsg := REGISTRATION + rcMsg + key
124 | return errors.New(errMsg)
125 | }
126 | key = rc.UserDataConfig.Code
127 | if USER_DATA != key {
128 | errMsg := USER_DATA + rcMsg + key
129 | return errors.New(errMsg)
130 | }
131 | return nil
132 | }
133 |
134 | func validateRegistrationTx(useCaseConfig UseCaseConfig) error {
135 | rc := useCaseConfig.RegistrationTx
136 | key := rc.Code
137 | rcMsg := " in validateRegistrationTx doesn't match key = "
138 | if REGISTRATION_TX != key {
139 | errMsg := REGISTRATION_TX + rcMsg + key
140 | return errors.New(errMsg)
141 | }
142 | key = rc.UserDataConfig.Code
143 | if USER_DATA != key {
144 | errMsg := USER_DATA + rcMsg + key
145 | return errors.New(errMsg)
146 | }
147 | return nil
148 | }
149 |
150 | func validateListUser(useCaseConfig UseCaseConfig) error {
151 | lc := useCaseConfig.ListUser
152 | key := lc.Code
153 | luMsg := " in validateListUser doesn't match key = "
154 | if LIST_USER != key {
155 | errMsg := LIST_USER + luMsg + key
156 | return errors.New(errMsg)
157 | }
158 | key = lc.CacheDataConfig.Code
159 | if CACHE_DATA != key {
160 | errMsg := CACHE_DATA + luMsg + key
161 | return errors.New(errMsg)
162 | }
163 | return nil
164 | }
165 |
166 |
--------------------------------------------------------------------------------
/app/container/container.go:
--------------------------------------------------------------------------------
1 | // package container use dependency injection to create concrete type and wire the whole application together
2 | package container
3 |
4 | type Container interface {
5 | // BuildUseCase creates concrete types for use case and it is included types.
6 | // For each call, it will create a new instance, which means it is not a singleton
7 | // Only exceptions are data store handlers, which are singletons. They are cached in container.
8 | BuildUseCase(code string) (interface{}, error)
9 |
10 | // This should only be used by container and it's sub-package
11 | // Get instance by code from container. Only data store handler can be retrieved from container
12 | Get(code string) (interface{}, bool)
13 |
14 | // This should only be used by container and it's sub-package
15 | // Put value into container with code as the key. Only data store handler is saved in container
16 | Put(code string, value interface{})
17 | }
18 |
--------------------------------------------------------------------------------
/app/container/containerhelper/serviceTmplContainer.go:
--------------------------------------------------------------------------------
1 | package containerhelper
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/app/config"
5 | "github.com/jfeng45/servicetmpl1/app/container"
6 | "github.com/jfeng45/servicetmpl1/domain/usecase"
7 | "github.com/pkg/errors"
8 | )
9 |
10 | func GetListUserUseCase(c container.Container) (usecase.ListUserUseCaseInterface, error) {
11 | key := config.LIST_USER
12 | value, err := c.BuildUseCase(key)
13 | if err != nil {
14 | //logger.Log.Errorf("%+v\n", err)
15 | return nil, errors.Wrap(err, "")
16 | }
17 | return value.(usecase.ListUserUseCaseInterface), nil
18 | }
19 |
20 | func GetRegistrationUseCase(c container.Container) (usecase.RegistrationUseCaseInterface, error) {
21 | key := config.REGISTRATION
22 | value, err := c.BuildUseCase(key)
23 | if err != nil {
24 | //logger.Log.Errorf("%+v\n", err)
25 | return nil, errors.Wrap(err, "")
26 | }
27 | return value.(usecase.RegistrationUseCaseInterface), nil
28 |
29 | }
30 |
31 | func GetRegistrationTxUseCase(c container.Container) (usecase.RegistrationTxUseCaseInterface, error) {
32 | key := config.REGISTRATION_TX
33 | value, err := c.BuildUseCase(key)
34 | if err != nil {
35 | //logger.Log.Errorf("%+v\n", err)
36 | return nil, errors.Wrap(err, "")
37 | }
38 | return value.(usecase.RegistrationTxUseCaseInterface), nil
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/app/container/dataservicefactory/cacheDataServiceFacory.go:
--------------------------------------------------------------------------------
1 | package dataservicefactory
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/app/config"
5 | "github.com/jfeng45/servicetmpl1/app/container"
6 | "github.com/jfeng45/servicetmpl1/app/container/datastorefactory"
7 | "github.com/jfeng45/servicetmpl1/app/logger"
8 | "github.com/jfeng45/servicetmpl1/applicationservice/cacheclient"
9 | "github.com/pkg/errors"
10 | "google.golang.org/grpc"
11 | )
12 |
13 | // cacheDataServiceFactory is a empty receiver for Build method
14 | type cacheDataServiceFactory struct{}
15 |
16 | func (cdsf *cacheDataServiceFactory) Build(c container.Container, dataConfig *config.DataConfig) (DataServiceInterface, error) {
17 | logger.Log.Debug("cacheDataServiceFactory")
18 | dsc := dataConfig.DataStoreConfig
19 | dsi, err := datastorefactory.GetDataStoreFb(dsc.Code).Build(c, &dsc)
20 | grpcConn := dsi.(*grpc.ClientConn)
21 | if err != nil {
22 | return nil, errors.Wrap(err, "")
23 | }
24 | cdg := cacheclient.CacheDataGrpc{grpcConn}
25 | //logger.Log.Debug("udm:", udm.DB)
26 |
27 | return &cdg, nil
28 | }
29 |
--------------------------------------------------------------------------------
/app/container/dataservicefactory/dataServiceFactory.go:
--------------------------------------------------------------------------------
1 | // Package dataservicefactory using factory method pattern to create concrete type to provide persistence service
2 | // The source of data can come from database ( for domain model "user") or from other service ( for domain model cache,
3 | // which comes from a gRPC service).
4 | // There is only one method Build() for the factory and all different types of data service following the same interface
5 | // to build the data service.
6 |
7 | package dataservicefactory
8 |
9 | import (
10 | "github.com/jfeng45/servicetmpl1/app/config"
11 | "github.com/jfeng45/servicetmpl1/app/container"
12 | )
13 |
14 | // To map "data service code" to "data service interface builder"
15 | // Each data service need a separate builder
16 | // Concrete builder is in corresponding factory file. For example, "courseDataServiceFactory" is in
17 | // "courseDataServiceFactory.go"
18 | var dsFbMap = map[string]dataServiceFbInterface{
19 | config.USER_DATA: &userDataServiceFactoryWrapper{},
20 | config.CACHE_DATA: &cacheDataServiceFactory{},
21 | }
22 |
23 | // DataServiceInterface serves as a marker to indicate the return type for Build method
24 | type DataServiceInterface interface{}
25 |
26 | // The builder interface for factory method pattern
27 | // Every factory needs to implement Build method
28 | type dataServiceFbInterface interface {
29 | Build(container.Container, *config.DataConfig) (DataServiceInterface, error)
30 | }
31 |
32 | // GetDataServiceFb is accessors for factoryBuilderMap
33 | func GetDataServiceFb(key string) dataServiceFbInterface {
34 | return dsFbMap[key]
35 | }
36 |
--------------------------------------------------------------------------------
/app/container/dataservicefactory/userDataServiceFactoryWrapper.go:
--------------------------------------------------------------------------------
1 | package dataservicefactory
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/app/config"
5 | "github.com/jfeng45/servicetmpl1/app/container"
6 | "github.com/jfeng45/servicetmpl1/app/container/dataservicefactory/userdataservicefactory"
7 | "github.com/jfeng45/servicetmpl1/app/logger"
8 | "github.com/pkg/errors"
9 | )
10 |
11 | // userDataServiceFactory is a empty receiver for Build method
12 | type userDataServiceFactoryWrapper struct{}
13 |
14 | func (udsfw *userDataServiceFactoryWrapper) Build(c container.Container, dataConfig *config.DataConfig) (DataServiceInterface, error) {
15 | logger.Log.Debug("UserDataServiceFactory")
16 | key := dataConfig.DataStoreConfig.Code
17 | udsi, err := userdataservicefactory.GetUserDataServiceFb(key).Build(c, dataConfig)
18 | if err != nil {
19 | return nil, errors.Wrap(err, "")
20 | }
21 | return udsi, nil
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/app/container/dataservicefactory/userdataservicefactory/couchdbUserDataServiceFactory.go:
--------------------------------------------------------------------------------
1 | package userdataservicefactory
2 |
3 | import (
4 | "github.com/go-kivik/kivik"
5 | "github.com/jfeng45/servicetmpl1/app/config"
6 | "github.com/jfeng45/servicetmpl1/app/container"
7 | "github.com/jfeng45/servicetmpl1/app/container/datastorefactory"
8 | "github.com/jfeng45/servicetmpl1/app/logger"
9 | "github.com/jfeng45/servicetmpl1/applicationservice/dataservice"
10 | "github.com/jfeng45/servicetmpl1/applicationservice/dataservice/userdata/couchdb"
11 | "github.com/pkg/errors"
12 | )
13 |
14 | // couchdbUserDataServiceFactory is a empty receiver for Build method
15 | type couchdbUserDataServiceFactory struct{}
16 |
17 | func (cudsf *couchdbUserDataServiceFactory) Build(c container.Container, dataConfig *config.DataConfig) (dataservice.UserDataInterface, error) {
18 | logger.Log.Debug("couchdbUserDataServiceFactory")
19 | dsc := dataConfig.DataStoreConfig
20 | dsi, err := datastorefactory.GetDataStoreFb(dsc.Code).Build(c, &dsc)
21 | if err != nil {
22 | return nil, errors.Wrap(err, "")
23 | }
24 | ds := dsi.(*kivik.DB)
25 | udc := couchdb.UserDataCouchdb{DB: ds}
26 | return &udc, nil
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/app/container/dataservicefactory/userdataservicefactory/sqlUserDataServiceFactory.go:
--------------------------------------------------------------------------------
1 | package userdataservicefactory
2 |
3 | import (
4 | "github.com/jfeng45/gtransaction/gdbc"
5 | "github.com/jfeng45/servicetmpl1/app/config"
6 | "github.com/jfeng45/servicetmpl1/app/container"
7 | "github.com/jfeng45/servicetmpl1/app/container/datastorefactory"
8 | "github.com/jfeng45/servicetmpl1/app/logger"
9 | "github.com/jfeng45/servicetmpl1/applicationservice/dataservice"
10 | "github.com/jfeng45/servicetmpl1/applicationservice/dataservice/userdata/sqldb"
11 | "github.com/pkg/errors"
12 | )
13 |
14 | // sqlUserDataServiceFactory is a empty receiver for Build method
15 | type sqlUserDataServiceFactory struct{}
16 |
17 | func (sudsf *sqlUserDataServiceFactory) Build(c container.Container, dataConfig *config.DataConfig) (dataservice.UserDataInterface, error) {
18 | logger.Log.Debug("sqlUserDataServiceFactory")
19 | dsc := dataConfig.DataStoreConfig
20 | dsi, err := datastorefactory.GetDataStoreFb(dsc.Code).Build(c, &dsc)
21 | if err != nil {
22 | return nil, errors.Wrap(err, "")
23 | }
24 | ds := dsi.(gdbc.SqlGdbc)
25 | uds := sqldb.UserDataSql{DB: ds}
26 | logger.Log.Debug("uds:", uds.DB)
27 | return &uds, nil
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/app/container/dataservicefactory/userdataservicefactory/userDataServiceFactory.go:
--------------------------------------------------------------------------------
1 | package userdataservicefactory
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/app/config"
5 | "github.com/jfeng45/servicetmpl1/app/container"
6 | "github.com/jfeng45/servicetmpl1/applicationservice/dataservice"
7 | )
8 |
9 | var udsFbMap = map[string]userDataServiceFbInterface{
10 | config.SQLDB: &sqlUserDataServiceFactory{},
11 | config.COUCHDB: &couchdbUserDataServiceFactory{},
12 | }
13 |
14 | // The builder interface for factory method pattern
15 | // Every factory needs to implement Build method
16 | type userDataServiceFbInterface interface {
17 | Build(container.Container, *config.DataConfig) (dataservice.UserDataInterface, error)
18 | }
19 |
20 | // GetDataServiceFb is accessors for factoryBuilderMap
21 | func GetUserDataServiceFb(key string) userDataServiceFbInterface {
22 | return udsFbMap[key]
23 | }
24 |
--------------------------------------------------------------------------------
/app/container/datastorefactory/cacheGrpcFactory.go:
--------------------------------------------------------------------------------
1 | package datastorefactory
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/app/config"
5 | "github.com/jfeng45/servicetmpl1/app/container"
6 | "github.com/jfeng45/servicetmpl1/app/logger"
7 | "github.com/pkg/errors"
8 | "google.golang.org/grpc"
9 | )
10 |
11 | // cacheGrpcFactory is an empty receiver for Build method
12 | type cacheGrpcFactory struct{}
13 |
14 | func (cgf *cacheGrpcFactory) Build(c container.Container, dsc *config.DataStoreConfig) (DataStoreInterface, error) {
15 | key := dsc.Code
16 | //if it is already in container, return
17 | if value, found := c.Get(key); found {
18 | logger.Log.Debug("find CacheGrpc key=%v \n", key)
19 | return value.(*grpc.ClientConn), nil
20 | }
21 | //not in map, need to create one
22 | logger.Log.Debug("doesn't find cacheGrpc key=%v need to created a new one\n", key)
23 |
24 | conn, err := grpc.Dial(dsc.UrlAddress, grpc.WithInsecure())
25 | if err != nil {
26 | return nil, errors.Wrap(err, "")
27 | }
28 | c.Put(key, conn)
29 | return conn, err
30 | }
31 |
--------------------------------------------------------------------------------
/app/container/datastorefactory/couchdbFactory.go:
--------------------------------------------------------------------------------
1 | package datastorefactory
2 |
3 | import (
4 | "context"
5 | couchdbKivid "github.com/go-kivik/couchdb"
6 | "github.com/go-kivik/kivik"
7 | "github.com/jfeng45/servicetmpl1/app/container"
8 | "github.com/jfeng45/servicetmpl1/app/logger"
9 |
10 | //"github.com/flimzy/kivik"
11 | "github.com/jfeng45/servicetmpl1/app/config"
12 | "github.com/pkg/errors"
13 | )
14 |
15 | // couchdbFactory is receiver for Build method
16 | type couchdbFactory struct{}
17 |
18 | // implement Build method for CouchDB database
19 | func (cf *couchdbFactory) Build(c container.Container, dsc *config.DataStoreConfig) (DataStoreInterface, error) {
20 | logger.Log.Debug("couchdbFactory")
21 | key := dsc.Code
22 |
23 | //if it is already in container, return
24 | if value, found := c.Get(key); found {
25 | logger.Log.Debug("found couchdb in container for key:", key)
26 | return value.(*kivik.DB), nil
27 | }
28 | // Don't know why needs adding the following line, because the driver is already registered in init() in couchdbKiv
29 | // however, not adding this, I got the error "unknown driver "couch" (forgotten import?)"
30 | kivik.Register(config.COUCHDB, &couchdbKivid.Couch{})
31 |
32 | client, err := kivik.New(context.TODO(), dsc.Code, dsc.UrlAddress)
33 | if err != nil {
34 | return nil, errors.Wrap(err, "")
35 | }
36 | db, err := client.DB(context.TODO(), dsc.DbName)
37 | if err != nil {
38 | return nil, errors.Wrap(err, "")
39 | }
40 | c.Put(key, db)
41 | return db, nil
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/app/container/datastorefactory/datastoreFactory.go:
--------------------------------------------------------------------------------
1 | // Package datastorefactory using factory method pattern to create concrete database handler.
2 | // Datastore can be a database or a service ( for example, gRPC service or RESTFul service), which provides data access
3 | // for domain model.
4 | // There is only one method Build() for the factory and all different types of store following the same interface
5 | // to build the store connection.
6 | // Generally speaking, each data store need a separate factory.
7 | package datastorefactory
8 |
9 | import (
10 | "github.com/jfeng45/servicetmpl1/app/config"
11 | "github.com/jfeng45/servicetmpl1/app/container"
12 | )
13 |
14 |
15 | // To map "database code" to "database interface builder"
16 | // Concreate builder is in corresponding factory file. For example, "sqlFactory" is in "sqlFactory".go
17 | var dsFbMap = map[string]dsFbInterface{
18 | config.SQLDB: &sqlFactory{},
19 | config.COUCHDB: &couchdbFactory{},
20 | config.CACHE_GRPC: &cacheGrpcFactory{},
21 | }
22 |
23 | // DataStoreInterface serve as a marker to indicate the return type for Build method
24 | type DataStoreInterface interface{}
25 |
26 | // The builder interface for factory method pattern
27 | // Every factory needs to implement Build method
28 | type dsFbInterface interface {
29 | Build(container.Container, *config.DataStoreConfig) (DataStoreInterface, error)
30 | }
31 |
32 | //GetDataStoreFb is accessors for factoryBuilderMap
33 | func GetDataStoreFb(key string) dsFbInterface {
34 | return dsFbMap[key]
35 | }
36 |
--------------------------------------------------------------------------------
/app/container/datastorefactory/sqlFactory.go:
--------------------------------------------------------------------------------
1 | package datastorefactory
2 |
3 | import (
4 | "database/sql"
5 |
6 | databaseConfig "github.com/jfeng45/gtransaction/config"
7 | "github.com/jfeng45/gtransaction/factory"
8 | "github.com/jfeng45/gtransaction/gdbc"
9 |
10 | "github.com/jfeng45/servicetmpl1/app/config"
11 | "github.com/jfeng45/servicetmpl1/app/container"
12 | "github.com/jfeng45/servicetmpl1/app/logger"
13 | )
14 |
15 | // sqlFactory is receiver for Build method
16 | type sqlFactory struct{}
17 |
18 | // implement Build method for SQL database
19 | func (sf *sqlFactory) Build(c container.Container, dsc *config.DataStoreConfig) (DataStoreInterface, error) {
20 | logger.Log.Debug("sqlFactory")
21 | key := dsc.Code
22 | // Only non-transaction connection is cached
23 | if !dsc.Tx {
24 | if value, found := c.Get(key); found {
25 | logger.Log.Debug("found db in container for key:", key)
26 | return value, nil
27 | }
28 | }
29 | tdbc := databaseConfig.DatabaseConfig{dsc.DriverName,dsc.UrlAddress, dsc.Tx}
30 | db, err := factory.BuildSqlDB(&tdbc)
31 | if err != nil {
32 | return nil, err
33 | }
34 | gdbc, err := buildGdbc(db, dsc.Tx)
35 | if err != nil {
36 | return nil, err
37 | }
38 | // Only non-transaction connection is cached
39 | if !dsc.Tx {
40 | c.Put(key, gdbc)
41 | }
42 | return gdbc, nil
43 |
44 | }
45 |
46 | func buildGdbc(sdb *sql.DB, tx bool) (gdbc.SqlGdbc, error) {
47 | var sdt gdbc.SqlGdbc
48 | if tx {
49 | tx, err := sdb.Begin()
50 | if err != nil {
51 | return nil, err
52 | }
53 | sdt = &gdbc.SqlConnTx{DB: tx}
54 | logger.Log.Debug("buildGdbc(), create TX:")
55 | } else {
56 | sdt = &gdbc.SqlDBTx{sdb}
57 | logger.Log.Debug("buildGdbc(), create DB:")
58 | }
59 | return sdt, nil
60 | }
61 |
--------------------------------------------------------------------------------
/app/container/servicecontainer/serviceContainer.go:
--------------------------------------------------------------------------------
1 | package servicecontainer
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/app/config"
5 | "github.com/jfeng45/servicetmpl1/app/container/usecasefactory"
6 | )
7 |
8 | type ServiceContainer struct {
9 | FactoryMap map[string]interface{}
10 | AppConfig *config.AppConfig
11 | }
12 |
13 | func (sc *ServiceContainer) BuildUseCase(code string) (interface{}, error) {
14 | return usecasefactory.GetUseCaseFb(code).Build(sc, sc.AppConfig, code)
15 | }
16 |
17 | func (sc *ServiceContainer) Get(code string) (interface{}, bool) {
18 | value, found := sc.FactoryMap[code]
19 | return value, found
20 | }
21 |
22 | func (sc *ServiceContainer) Put(code string, value interface{}) {
23 | sc.FactoryMap[code] = value
24 | }
25 |
--------------------------------------------------------------------------------
/app/container/usecasefactory/listUserFactory.go:
--------------------------------------------------------------------------------
1 | package usecasefactory
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/app/config"
5 | "github.com/jfeng45/servicetmpl1/app/container"
6 | "github.com/jfeng45/servicetmpl1/domain/usecase/listuser"
7 | "github.com/pkg/errors"
8 | )
9 |
10 | type ListUserFactory struct{}
11 |
12 | func (luf *ListUserFactory) Build(c container.Container, appConfig *config.AppConfig, key string) (UseCaseInterface, error) {
13 | uc := appConfig.UseCaseConfig.ListUser
14 |
15 | udi, err := buildUserData(c, &uc.UserDataConfig)
16 | if err != nil {
17 | return nil, errors.Wrap(err, "")
18 | }
19 | cdi, err := buildCacheData(c, &uc.CacheDataConfig)
20 | luuc := listuser.ListUserUseCase{UserDataInterface: udi, CacheDataInterface: cdi}
21 | return &luuc, nil
22 | }
23 |
--------------------------------------------------------------------------------
/app/container/usecasefactory/registrationFactory.go:
--------------------------------------------------------------------------------
1 | package usecasefactory
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/app/config"
5 | "github.com/jfeng45/servicetmpl1/app/container"
6 | "github.com/jfeng45/servicetmpl1/domain/usecase/registration"
7 | "github.com/pkg/errors"
8 | )
9 |
10 | type RegistrationFactory struct {
11 | }
12 |
13 | // Build creates concrete type for RegistrationUseCaseInterface
14 | func (rf *RegistrationFactory) Build(c container.Container, appConfig *config.AppConfig, key string) (UseCaseInterface, error) {
15 | uc := appConfig.UseCaseConfig.Registration
16 | udi, err := buildUserData(c, &uc.UserDataConfig)
17 | if err != nil {
18 | return nil, errors.Wrap(err, "")
19 | }
20 | ruc := registration.RegistrationUseCase{UserDataInterface: udi}
21 |
22 | return &ruc, nil
23 | }
24 |
--------------------------------------------------------------------------------
/app/container/usecasefactory/registrationTxFactory.go:
--------------------------------------------------------------------------------
1 | package usecasefactory
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/app/config"
5 | "github.com/jfeng45/servicetmpl1/app/container"
6 | "github.com/jfeng45/servicetmpl1/domain/usecase/registration"
7 | "github.com/pkg/errors"
8 | )
9 |
10 | type RegistrationTxFactory struct {
11 | }
12 |
13 | // Build creates concrete type for RegistrationTxUseCaseInterface
14 | func (rtf *RegistrationTxFactory) Build(c container.Container, appConfig *config.AppConfig, key string) (UseCaseInterface, error) {
15 | uc := appConfig.UseCaseConfig.RegistrationTx
16 | udi, err := buildUserData(c, &uc.UserDataConfig)
17 | if err != nil {
18 | return nil, errors.Wrap(err, "")
19 | }
20 | ruc := registration.RegistrationTxUseCase{UserDataInterface: udi}
21 |
22 | return &ruc, nil
23 | }
24 |
--------------------------------------------------------------------------------
/app/container/usecasefactory/useCaseFactory.go:
--------------------------------------------------------------------------------
1 | // Package usecasefactory using factory method pattern to create concrete case case.
2 | // Generally speaking, each use case needs a separate factory.
3 | package usecasefactory
4 |
5 | import (
6 | "github.com/jfeng45/servicetmpl1/app/config"
7 | "github.com/jfeng45/servicetmpl1/app/container"
8 | )
9 |
10 | //To map "use case code" to "use case interface builder"
11 | // Each use case has exactly one factory. For example, "registration" use case has "RegistrationFactory"
12 | // Each factory has it's own file. For example, "RegistrationFactory" is in "registrationFactory.go"
13 | var UseCaseFactoryBuilderMap = map[string]UseCaseFbInterface{
14 | config.REGISTRATION: &RegistrationFactory{},
15 | config.LIST_USER: &ListUserFactory{},
16 | config.REGISTRATION_TX: &RegistrationTxFactory{},
17 | }
18 |
19 | // UseCaseInterface serve as a marker to indicate the return type for Build method
20 | type UseCaseInterface interface{}
21 |
22 | // The builder interface for factory method pattern
23 | // Every factory needs to implement build method
24 | type UseCaseFbInterface interface {
25 | Build(c container.Container, appConfig *config.AppConfig, key string) (UseCaseInterface, error)
26 | }
27 |
28 | //GetDataStoreFb is accessors for factoryBuilderMap
29 | func GetUseCaseFb(key string) UseCaseFbInterface {
30 | return UseCaseFactoryBuilderMap[key]
31 | }
32 |
--------------------------------------------------------------------------------
/app/container/usecasefactory/useCaseHelper.go:
--------------------------------------------------------------------------------
1 | package usecasefactory
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/app/config"
5 | "github.com/jfeng45/servicetmpl1/app/container"
6 | "github.com/jfeng45/servicetmpl1/app/container/dataservicefactory"
7 | "github.com/jfeng45/servicetmpl1/applicationservice/dataservice"
8 | "github.com/pkg/errors"
9 | )
10 |
11 | func buildUserData(c container.Container, dc *config.DataConfig) (dataservice.UserDataInterface, error) {
12 | dsi, err := dataservicefactory.GetDataServiceFb(dc.Code).Build(c, dc)
13 | if err != nil {
14 | return nil, errors.Wrap(err, "")
15 | }
16 | udi := dsi.(dataservice.UserDataInterface)
17 | return udi, nil
18 | }
19 |
20 | func buildCacheData(c container.Container, dc *config.DataConfig) (dataservice.CacheDataInterface, error) {
21 | //logger.Log.Debug("uc:", cdc)
22 | dsi, err := dataservicefactory.GetDataServiceFb(dc.Code).Build(c, dc)
23 | if err != nil {
24 | return nil, errors.Wrap(err, "")
25 | }
26 | cdi := dsi.(dataservice.CacheDataInterface)
27 | return cdi, nil
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/app/logger/logger.go:
--------------------------------------------------------------------------------
1 | // Package logger represents a generic logging interface
2 | package logger
3 |
4 | import "github.com/jfeng45/glogger"
5 |
6 | // Log is a package level variable, every program should access logging function through "Log"
7 | var Log glogger.Logger
8 |
9 | // SetLogger is the setter for log variable, it should be the only way to assign value to log
10 | func SetLogger(newLogger glogger.Logger) {
11 | Log = newLogger
12 | }
13 |
--------------------------------------------------------------------------------
/applicationservice/cacheclient/cacheClient.go:
--------------------------------------------------------------------------------
1 | // Package cacheclient is the wrapper around the third party gRPC Cache Micro-service.
2 | // It encapsulates the logic to call outside service, to make it transparent to the business logic layer.
3 |
4 | package cacheclient
5 |
6 | import (
7 | "context"
8 | "github.com/jfeng45/servicetmpl1/app/logger"
9 | "github.com/jfeng45/servicetmpl1/applicationservice/cacheclient/generatedclient"
10 | "google.golang.org/grpc"
11 | )
12 |
13 | // CacheDataGrpc represents the gRPC connection handler
14 | type CacheDataGrpc struct {
15 | Conn *grpc.ClientConn
16 | }
17 |
18 | // getCacheClient creates a gRPC client
19 | func getCacheClient(conn *grpc.ClientConn) generatedclient.CacheServiceClient {
20 | return generatedclient.NewCacheServiceClient(conn)
21 | }
22 |
23 | // Get makes a call to Get function on Cache service
24 | func (cdg CacheDataGrpc) Get(key string) ([]byte, error) {
25 | cacheClient := getCacheClient(cdg.Conn)
26 | resp, err := cacheClient.Get(context.Background(), &generatedclient.GetReq{Key: key})
27 | if err != nil {
28 | return nil, err
29 | } else {
30 | return resp.Value, err
31 | }
32 | }
33 |
34 | // Store makes a call to Store function on Cache service
35 | func (cdg CacheDataGrpc) Store(key string, value []byte) error {
36 | cacheClient := getCacheClient(cdg.Conn)
37 | ctx := context.Background()
38 | _, err := cacheClient.Store(ctx, &generatedclient.StoreReq{Key: key, Value: value})
39 |
40 | if err != nil {
41 | return err
42 | } else {
43 | logger.Log.Debug("store called")
44 | }
45 | return nil
46 | }
47 |
--------------------------------------------------------------------------------
/applicationservice/cacheclient/generatedclient/cacheJin.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: cacheJin.proto
3 |
4 | package generatedclient
5 |
6 | import proto "github.com/golang/protobuf/proto"
7 | import fmt "fmt"
8 | import math "math"
9 |
10 | import (
11 | context "golang.org/x/net/context"
12 | grpc "google.golang.org/grpc"
13 | )
14 |
15 | // Reference imports to suppress errors if they are not otherwise used.
16 | var _ = proto.Marshal
17 | var _ = fmt.Errorf
18 | var _ = math.Inf
19 |
20 | // This is a compile-time assertion to ensure that this generated file
21 | // is compatible with the proto package it is being compiled against.
22 | // A compilation error at this line likely means your copy of the
23 | // proto package needs to be updated.
24 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
25 |
26 | type DumpReq struct {
27 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
28 | XXX_unrecognized []byte `json:"-"`
29 | XXX_sizecache int32 `json:"-"`
30 | }
31 |
32 | func (m *DumpReq) Reset() { *m = DumpReq{} }
33 | func (m *DumpReq) String() string { return proto.CompactTextString(m) }
34 | func (*DumpReq) ProtoMessage() {}
35 | func (*DumpReq) Descriptor() ([]byte, []int) {
36 | return fileDescriptor_cacheJin_de21aa9a66397cc7, []int{0}
37 | }
38 | func (m *DumpReq) XXX_Unmarshal(b []byte) error {
39 | return xxx_messageInfo_DumpReq.Unmarshal(m, b)
40 | }
41 | func (m *DumpReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
42 | return xxx_messageInfo_DumpReq.Marshal(b, m, deterministic)
43 | }
44 | func (dst *DumpReq) XXX_Merge(src proto.Message) {
45 | xxx_messageInfo_DumpReq.Merge(dst, src)
46 | }
47 | func (m *DumpReq) XXX_Size() int {
48 | return xxx_messageInfo_DumpReq.Size(m)
49 | }
50 | func (m *DumpReq) XXX_DiscardUnknown() {
51 | xxx_messageInfo_DumpReq.DiscardUnknown(m)
52 | }
53 |
54 | var xxx_messageInfo_DumpReq proto.InternalMessageInfo
55 |
56 | type DumpItem struct {
57 | Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
58 | Val []byte `protobuf:"bytes,2,opt,name=val,proto3" json:"val,omitempty"`
59 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
60 | XXX_unrecognized []byte `json:"-"`
61 | XXX_sizecache int32 `json:"-"`
62 | }
63 |
64 | func (m *DumpItem) Reset() { *m = DumpItem{} }
65 | func (m *DumpItem) String() string { return proto.CompactTextString(m) }
66 | func (*DumpItem) ProtoMessage() {}
67 | func (*DumpItem) Descriptor() ([]byte, []int) {
68 | return fileDescriptor_cacheJin_de21aa9a66397cc7, []int{1}
69 | }
70 | func (m *DumpItem) XXX_Unmarshal(b []byte) error {
71 | return xxx_messageInfo_DumpItem.Unmarshal(m, b)
72 | }
73 | func (m *DumpItem) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
74 | return xxx_messageInfo_DumpItem.Marshal(b, m, deterministic)
75 | }
76 | func (dst *DumpItem) XXX_Merge(src proto.Message) {
77 | xxx_messageInfo_DumpItem.Merge(dst, src)
78 | }
79 | func (m *DumpItem) XXX_Size() int {
80 | return xxx_messageInfo_DumpItem.Size(m)
81 | }
82 | func (m *DumpItem) XXX_DiscardUnknown() {
83 | xxx_messageInfo_DumpItem.DiscardUnknown(m)
84 | }
85 |
86 | var xxx_messageInfo_DumpItem proto.InternalMessageInfo
87 |
88 | func (m *DumpItem) GetKey() string {
89 | if m != nil {
90 | return m.Key
91 | }
92 | return ""
93 | }
94 |
95 | func (m *DumpItem) GetVal() []byte {
96 | if m != nil {
97 | return m.Val
98 | }
99 | return nil
100 | }
101 |
102 | type StoreReq struct {
103 | Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
104 | Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"`
105 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
106 | XXX_unrecognized []byte `json:"-"`
107 | XXX_sizecache int32 `json:"-"`
108 | }
109 |
110 | func (m *StoreReq) Reset() { *m = StoreReq{} }
111 | func (m *StoreReq) String() string { return proto.CompactTextString(m) }
112 | func (*StoreReq) ProtoMessage() {}
113 | func (*StoreReq) Descriptor() ([]byte, []int) {
114 | return fileDescriptor_cacheJin_de21aa9a66397cc7, []int{2}
115 | }
116 | func (m *StoreReq) XXX_Unmarshal(b []byte) error {
117 | return xxx_messageInfo_StoreReq.Unmarshal(m, b)
118 | }
119 | func (m *StoreReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
120 | return xxx_messageInfo_StoreReq.Marshal(b, m, deterministic)
121 | }
122 | func (dst *StoreReq) XXX_Merge(src proto.Message) {
123 | xxx_messageInfo_StoreReq.Merge(dst, src)
124 | }
125 | func (m *StoreReq) XXX_Size() int {
126 | return xxx_messageInfo_StoreReq.Size(m)
127 | }
128 | func (m *StoreReq) XXX_DiscardUnknown() {
129 | xxx_messageInfo_StoreReq.DiscardUnknown(m)
130 | }
131 |
132 | var xxx_messageInfo_StoreReq proto.InternalMessageInfo
133 |
134 | func (m *StoreReq) GetKey() string {
135 | if m != nil {
136 | return m.Key
137 | }
138 | return ""
139 | }
140 |
141 | func (m *StoreReq) GetValue() []byte {
142 | if m != nil {
143 | return m.Value
144 | }
145 | return nil
146 | }
147 |
148 | type StoreResp struct {
149 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
150 | XXX_unrecognized []byte `json:"-"`
151 | XXX_sizecache int32 `json:"-"`
152 | }
153 |
154 | func (m *StoreResp) Reset() { *m = StoreResp{} }
155 | func (m *StoreResp) String() string { return proto.CompactTextString(m) }
156 | func (*StoreResp) ProtoMessage() {}
157 | func (*StoreResp) Descriptor() ([]byte, []int) {
158 | return fileDescriptor_cacheJin_de21aa9a66397cc7, []int{3}
159 | }
160 | func (m *StoreResp) XXX_Unmarshal(b []byte) error {
161 | return xxx_messageInfo_StoreResp.Unmarshal(m, b)
162 | }
163 | func (m *StoreResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
164 | return xxx_messageInfo_StoreResp.Marshal(b, m, deterministic)
165 | }
166 | func (dst *StoreResp) XXX_Merge(src proto.Message) {
167 | xxx_messageInfo_StoreResp.Merge(dst, src)
168 | }
169 | func (m *StoreResp) XXX_Size() int {
170 | return xxx_messageInfo_StoreResp.Size(m)
171 | }
172 | func (m *StoreResp) XXX_DiscardUnknown() {
173 | xxx_messageInfo_StoreResp.DiscardUnknown(m)
174 | }
175 |
176 | var xxx_messageInfo_StoreResp proto.InternalMessageInfo
177 |
178 | type GetReq struct {
179 | Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"`
180 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
181 | XXX_unrecognized []byte `json:"-"`
182 | XXX_sizecache int32 `json:"-"`
183 | }
184 |
185 | func (m *GetReq) Reset() { *m = GetReq{} }
186 | func (m *GetReq) String() string { return proto.CompactTextString(m) }
187 | func (*GetReq) ProtoMessage() {}
188 | func (*GetReq) Descriptor() ([]byte, []int) {
189 | return fileDescriptor_cacheJin_de21aa9a66397cc7, []int{4}
190 | }
191 | func (m *GetReq) XXX_Unmarshal(b []byte) error {
192 | return xxx_messageInfo_GetReq.Unmarshal(m, b)
193 | }
194 | func (m *GetReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
195 | return xxx_messageInfo_GetReq.Marshal(b, m, deterministic)
196 | }
197 | func (dst *GetReq) XXX_Merge(src proto.Message) {
198 | xxx_messageInfo_GetReq.Merge(dst, src)
199 | }
200 | func (m *GetReq) XXX_Size() int {
201 | return xxx_messageInfo_GetReq.Size(m)
202 | }
203 | func (m *GetReq) XXX_DiscardUnknown() {
204 | xxx_messageInfo_GetReq.DiscardUnknown(m)
205 | }
206 |
207 | var xxx_messageInfo_GetReq proto.InternalMessageInfo
208 |
209 | func (m *GetReq) GetKey() string {
210 | if m != nil {
211 | return m.Key
212 | }
213 | return ""
214 | }
215 |
216 | type GetResp struct {
217 | Value []byte `protobuf:"bytes,1,opt,name=value,proto3" json:"value,omitempty"`
218 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
219 | XXX_unrecognized []byte `json:"-"`
220 | XXX_sizecache int32 `json:"-"`
221 | }
222 |
223 | func (m *GetResp) Reset() { *m = GetResp{} }
224 | func (m *GetResp) String() string { return proto.CompactTextString(m) }
225 | func (*GetResp) ProtoMessage() {}
226 | func (*GetResp) Descriptor() ([]byte, []int) {
227 | return fileDescriptor_cacheJin_de21aa9a66397cc7, []int{5}
228 | }
229 | func (m *GetResp) XXX_Unmarshal(b []byte) error {
230 | return xxx_messageInfo_GetResp.Unmarshal(m, b)
231 | }
232 | func (m *GetResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
233 | return xxx_messageInfo_GetResp.Marshal(b, m, deterministic)
234 | }
235 | func (dst *GetResp) XXX_Merge(src proto.Message) {
236 | xxx_messageInfo_GetResp.Merge(dst, src)
237 | }
238 | func (m *GetResp) XXX_Size() int {
239 | return xxx_messageInfo_GetResp.Size(m)
240 | }
241 | func (m *GetResp) XXX_DiscardUnknown() {
242 | xxx_messageInfo_GetResp.DiscardUnknown(m)
243 | }
244 |
245 | var xxx_messageInfo_GetResp proto.InternalMessageInfo
246 |
247 | func (m *GetResp) GetValue() []byte {
248 | if m != nil {
249 | return m.Value
250 | }
251 | return nil
252 | }
253 |
254 | func init() {
255 | proto.RegisterType((*DumpReq)(nil), "reservegrpc.DumpReq")
256 | proto.RegisterType((*DumpItem)(nil), "reservegrpc.DumpItem")
257 | proto.RegisterType((*StoreReq)(nil), "reservegrpc.StoreReq")
258 | proto.RegisterType((*StoreResp)(nil), "reservegrpc.StoreResp")
259 | proto.RegisterType((*GetReq)(nil), "reservegrpc.GetReq")
260 | proto.RegisterType((*GetResp)(nil), "reservegrpc.GetResp")
261 | }
262 |
263 | // Reference imports to suppress errors if they are not otherwise used.
264 | var _ context.Context
265 | var _ grpc.ClientConn
266 |
267 | // This is a compile-time assertion to ensure that this generated file
268 | // is compatible with the grpc package it is being compiled against.
269 | const _ = grpc.SupportPackageIsVersion4
270 |
271 | // CacheServiceClient is the client API for CacheService service.
272 | //
273 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
274 | type CacheServiceClient interface {
275 | Store(ctx context.Context, in *StoreReq, opts ...grpc.CallOption) (*StoreResp, error)
276 | Get(ctx context.Context, in *GetReq, opts ...grpc.CallOption) (*GetResp, error)
277 | Dump(ctx context.Context, in *DumpReq, opts ...grpc.CallOption) (CacheService_DumpClient, error)
278 | }
279 |
280 | type cacheServiceClient struct {
281 | cc *grpc.ClientConn
282 | }
283 |
284 | func NewCacheServiceClient(cc *grpc.ClientConn) CacheServiceClient {
285 | return &cacheServiceClient{cc}
286 | }
287 |
288 | func (c *cacheServiceClient) Store(ctx context.Context, in *StoreReq, opts ...grpc.CallOption) (*StoreResp, error) {
289 | out := new(StoreResp)
290 | err := c.cc.Invoke(ctx, "/reservegrpc.CacheService/Store", in, out, opts...)
291 | if err != nil {
292 | return nil, err
293 | }
294 | return out, nil
295 | }
296 |
297 | func (c *cacheServiceClient) Get(ctx context.Context, in *GetReq, opts ...grpc.CallOption) (*GetResp, error) {
298 | out := new(GetResp)
299 | err := c.cc.Invoke(ctx, "/reservegrpc.CacheService/Get", in, out, opts...)
300 | if err != nil {
301 | return nil, err
302 | }
303 | return out, nil
304 | }
305 |
306 | func (c *cacheServiceClient) Dump(ctx context.Context, in *DumpReq, opts ...grpc.CallOption) (CacheService_DumpClient, error) {
307 | stream, err := c.cc.NewStream(ctx, &_CacheService_serviceDesc.Streams[0], "/reservegrpc.CacheService/Dump", opts...)
308 | if err != nil {
309 | return nil, err
310 | }
311 | x := &cacheServiceDumpClient{stream}
312 | if err := x.ClientStream.SendMsg(in); err != nil {
313 | return nil, err
314 | }
315 | if err := x.ClientStream.CloseSend(); err != nil {
316 | return nil, err
317 | }
318 | return x, nil
319 | }
320 |
321 | type CacheService_DumpClient interface {
322 | Recv() (*DumpItem, error)
323 | grpc.ClientStream
324 | }
325 |
326 | type cacheServiceDumpClient struct {
327 | grpc.ClientStream
328 | }
329 |
330 | func (x *cacheServiceDumpClient) Recv() (*DumpItem, error) {
331 | m := new(DumpItem)
332 | if err := x.ClientStream.RecvMsg(m); err != nil {
333 | return nil, err
334 | }
335 | return m, nil
336 | }
337 |
338 | // CacheServiceServer is the server API for CacheService service.
339 | type CacheServiceServer interface {
340 | Store(context.Context, *StoreReq) (*StoreResp, error)
341 | Get(context.Context, *GetReq) (*GetResp, error)
342 | Dump(*DumpReq, CacheService_DumpServer) error
343 | }
344 |
345 | func RegisterCacheServiceServer(s *grpc.Server, srv CacheServiceServer) {
346 | s.RegisterService(&_CacheService_serviceDesc, srv)
347 | }
348 |
349 | func _CacheService_Store_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
350 | in := new(StoreReq)
351 | if err := dec(in); err != nil {
352 | return nil, err
353 | }
354 | if interceptor == nil {
355 | return srv.(CacheServiceServer).Store(ctx, in)
356 | }
357 | info := &grpc.UnaryServerInfo{
358 | Server: srv,
359 | FullMethod: "/reservegrpc.CacheService/Store",
360 | }
361 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
362 | return srv.(CacheServiceServer).Store(ctx, req.(*StoreReq))
363 | }
364 | return interceptor(ctx, in, info, handler)
365 | }
366 |
367 | func _CacheService_Get_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
368 | in := new(GetReq)
369 | if err := dec(in); err != nil {
370 | return nil, err
371 | }
372 | if interceptor == nil {
373 | return srv.(CacheServiceServer).Get(ctx, in)
374 | }
375 | info := &grpc.UnaryServerInfo{
376 | Server: srv,
377 | FullMethod: "/reservegrpc.CacheService/Get",
378 | }
379 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
380 | return srv.(CacheServiceServer).Get(ctx, req.(*GetReq))
381 | }
382 | return interceptor(ctx, in, info, handler)
383 | }
384 |
385 | func _CacheService_Dump_Handler(srv interface{}, stream grpc.ServerStream) error {
386 | m := new(DumpReq)
387 | if err := stream.RecvMsg(m); err != nil {
388 | return err
389 | }
390 | return srv.(CacheServiceServer).Dump(m, &cacheServiceDumpServer{stream})
391 | }
392 |
393 | type CacheService_DumpServer interface {
394 | Send(*DumpItem) error
395 | grpc.ServerStream
396 | }
397 |
398 | type cacheServiceDumpServer struct {
399 | grpc.ServerStream
400 | }
401 |
402 | func (x *cacheServiceDumpServer) Send(m *DumpItem) error {
403 | return x.ServerStream.SendMsg(m)
404 | }
405 |
406 | var _CacheService_serviceDesc = grpc.ServiceDesc{
407 | ServiceName: "reservegrpc.CacheService",
408 | HandlerType: (*CacheServiceServer)(nil),
409 | Methods: []grpc.MethodDesc{
410 | {
411 | MethodName: "Store",
412 | Handler: _CacheService_Store_Handler,
413 | },
414 | {
415 | MethodName: "Get",
416 | Handler: _CacheService_Get_Handler,
417 | },
418 | },
419 | Streams: []grpc.StreamDesc{
420 | {
421 | StreamName: "Dump",
422 | Handler: _CacheService_Dump_Handler,
423 | ServerStreams: true,
424 | },
425 | },
426 | Metadata: "cacheJin.proto",
427 | }
428 |
429 | func init() { proto.RegisterFile("cacheJin.proto", fileDescriptor_cacheJin_de21aa9a66397cc7) }
430 |
431 | var fileDescriptor_cacheJin_de21aa9a66397cc7 = []byte{
432 | // 242 bytes of a gzipped FileDescriptorProto
433 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x51, 0x4d, 0x4b, 0x03, 0x31,
434 | 0x10, 0x6d, 0xac, 0xfd, 0xd8, 0x69, 0x11, 0x89, 0xad, 0x94, 0x5c, 0x2c, 0x73, 0xea, 0x29, 0xc8,
435 | 0x7a, 0xd0, 0xbb, 0x42, 0xd1, 0x63, 0xfa, 0x0b, 0xd6, 0x30, 0x68, 0xb1, 0x75, 0xd3, 0x24, 0x0d,
436 | 0xf8, 0xc3, 0xfc, 0x7f, 0x92, 0x74, 0xa5, 0xbb, 0xec, 0xde, 0x5e, 0xde, 0xcc, 0x7b, 0xcc, 0x7b,
437 | 0x81, 0x2b, 0x5d, 0xe8, 0x4f, 0x7a, 0xdb, 0x7e, 0x4b, 0x63, 0x4b, 0x5f, 0xf2, 0x89, 0x25, 0x47,
438 | 0x36, 0xd0, 0x87, 0x35, 0x1a, 0x33, 0x18, 0xbd, 0x1c, 0xf7, 0x46, 0xd1, 0x01, 0x25, 0x8c, 0x23,
439 | 0x7c, 0xf5, 0xb4, 0xe7, 0xd7, 0xd0, 0xff, 0xa2, 0x9f, 0x05, 0x5b, 0xb2, 0x55, 0xa6, 0x22, 0x8c,
440 | 0x4c, 0x28, 0x76, 0x8b, 0x8b, 0x25, 0x5b, 0x4d, 0x55, 0x84, 0x98, 0xc3, 0x78, 0xe3, 0x4b, 0x4b,
441 | 0x8a, 0x0e, 0x1d, 0xfb, 0x33, 0x18, 0x84, 0x62, 0x77, 0xa4, 0x4a, 0x71, 0x7a, 0xe0, 0x04, 0xb2,
442 | 0x4a, 0xe3, 0x0c, 0x0a, 0x18, 0xae, 0xc9, 0x77, 0xca, 0xf1, 0x0e, 0x46, 0x69, 0xe6, 0xcc, 0xd9,
443 | 0x89, 0xd5, 0x9c, 0xf2, 0x5f, 0x06, 0xd3, 0xe7, 0x18, 0x6c, 0x43, 0x36, 0x6c, 0x35, 0xf1, 0x27,
444 | 0x18, 0x24, 0x6b, 0x3e, 0x97, 0xb5, 0x80, 0xf2, 0xff, 0x44, 0x71, 0xdb, 0x45, 0x3b, 0x83, 0x3d,
445 | 0x9e, 0x43, 0x7f, 0x4d, 0x9e, 0xdf, 0x34, 0x16, 0x4e, 0x97, 0x89, 0x59, 0x9b, 0x4c, 0x9a, 0x47,
446 | 0xb8, 0x8c, 0x65, 0xf1, 0xe6, 0xbc, 0xaa, 0x52, 0xcc, 0x5b, 0x6c, 0x6c, 0x15, 0x7b, 0xf7, 0xec,
447 | 0x7d, 0x98, 0x3e, 0xe1, 0xe1, 0x2f, 0x00, 0x00, 0xff, 0xff, 0x84, 0x90, 0x28, 0x3f, 0x96, 0x01,
448 | 0x00, 0x00,
449 | }
450 |
--------------------------------------------------------------------------------
/applicationservice/cacheclient/generatedclient/doc.go:
--------------------------------------------------------------------------------
1 | //Package generatedclient is created to save the generated client lib for gRPC call.
2 | package generatedclient
3 |
--------------------------------------------------------------------------------
/applicationservice/dataservice/dataService.go:
--------------------------------------------------------------------------------
1 | // Package dataservice and it's sub-package represents data persistence service, mainly access database,
2 | // but also including data persisted by other Micro-service.
3 | // For Micro-server, only the interface is defined in this package, the data transformation code is in adapter package
4 | // This is the top level package and it only defines interface, and all implementations are defined in sub-package
5 | // Use case package depends on it.
6 | package dataservice
7 |
8 | import (
9 | "github.com/jfeng45/gtransaction/txdataservice"
10 | "github.com/jfeng45/servicetmpl1/domain/model"
11 | )
12 |
13 | // UserDataInterface represents interface for user data access through database
14 | type UserDataInterface interface {
15 | // Remove deletes a user by user name from database.
16 | Remove(username string) (rowsAffected int64, err error)
17 | // Find retrieves a user from database based on a user's id
18 | Find(id int) (*model.User, error)
19 | // FindByName retrieves a user from database by User.Name
20 | FindByName(name string) (user *model.User, err error)
21 | // FindAll retrieves all users from database as an array of user
22 | FindAll() ([]model.User, error)
23 | // Update changes user information on the User.Id passed in.
24 | Update(user *model.User) (rowsAffected int64, err error)
25 | // Insert adds a user to a database. The returned resultUser has a Id, which is auto generated by database
26 | Insert(user *model.User) (resultUser *model.User, err error)
27 | // Add transaction support to the data interface
28 | txdataservice.TxDataInterface
29 | }
30 |
31 | // CacheDataInterface represents interface for cache service, which is a micro-service
32 | type CacheDataInterface interface {
33 | // Get handles call to Get function on Cache service
34 | Get(key string) ([]byte, error)
35 | // Store handles call to Store function on Cache service
36 | Store(key string, value []byte) error
37 | }
38 |
39 |
40 |
--------------------------------------------------------------------------------
/applicationservice/dataservice/userdata/couchdb/userDataCouchdb.go:
--------------------------------------------------------------------------------
1 | // Package couchdb represents the CouchDB implementation of the user data persistence layer
2 | package couchdb
3 |
4 | import (
5 | "context"
6 | "github.com/go-kivik/kivik"
7 | "github.com/jfeng45/servicetmpl1/app/logger"
8 | "github.com/jfeng45/servicetmpl1/domain/model"
9 | "github.com/pkg/errors"
10 | )
11 |
12 | const (
13 | DDOC string = "_design/serviceConfigDesignDoc"
14 | VIEW_ID string = "_view/serviceConfigByID"
15 | )
16 |
17 | type UserDataCouchdb struct {
18 | DB *kivik.DB
19 | }
20 |
21 | // Create a view for "Find()", only need to run once.
22 | // Should be created by Fauxton, but you may not know how to do it. To make it easy for you, put it in code.
23 | // This function is created to make it easy to run the application, don't do it in production code
24 | // When run for more than once, it will show error "Conflict: Document update conflict", just ignore it
25 | func createView(udc *UserDataCouchdb) {
26 | rev, err := udc.DB.Put(context.TODO(), DDOC, map[string]interface{}{
27 | "_id": DDOC,
28 | "views": map[string]interface{}{
29 | "serviceConfigByID": map[string]interface{}{
30 | "map": "function(doc) {\n if (doc.uid) {\n emit(doc.uid, doc);\n}\n}",
31 | },
32 | },
33 | "language": "javascript",
34 | })
35 | // For each rnu after first, it will throw an error because it already exist. Just ignore it.
36 | if err != nil {
37 | logger.Log.Errorf("err:%v\n", err)
38 | }
39 | logger.Log.Debug("rev:", rev)
40 | }
41 |
42 | func (udc *UserDataCouchdb) Find(id int) (*model.User, error) {
43 | var err error
44 | // only need to do it once
45 | createView(udc)
46 | rows, err := udc.DB.Query(context.TODO(), DDOC, VIEW_ID, map[string]interface{}{"reduce": false},
47 | kivik.Options{"key": id})
48 |
49 | if err != nil {
50 | return nil, errors.Wrap(err, "")
51 | }
52 | var user *model.User
53 | if rows.Next() {
54 | user = &model.User{}
55 | if err := rows.ScanValue(user); err != nil {
56 | return nil, errors.Wrap(err, "")
57 | }
58 | }
59 | logger.Log.Debugf("view:%+v", user)
60 |
61 | if rows.Err() != nil {
62 | return nil, errors.Wrap(rows.Err(), "")
63 | }
64 | return user, nil
65 | }
66 |
67 | //The simple version (no need for view) of Find() to get it work, it is kind cheating because it didn't use the parameter id.
68 | //func (udc *UserDataCouchdb) Find(id int) (*model.User, error) {
69 | // _id :="80a9134c7dfa53f67f6be214e1000fa7"
70 | // row, err :=udc.DB.Get(context.TODO(), _id)
71 | // if err != nil {
72 | // return nil, errors.Wrap(err, "")
73 | // }
74 | // var user model.User
75 | // if err=row.ScanDoc(&user); err!=nil {
76 | // panic(err)
77 | // }
78 | // logger.Log.Debugf("user:", user)
79 | // return &user, nil
80 | //}
81 |
82 | func (udc *UserDataCouchdb) Remove(username string) (int64, error) {
83 |
84 | return 0, nil
85 | }
86 | func (udc *UserDataCouchdb) Update(user *model.User) (int64, error) {
87 | return 0, nil
88 | }
89 |
90 | func (udc *UserDataCouchdb) Insert(user *model.User) (*model.User, error) {
91 | return nil, nil
92 | }
93 | func (udc *UserDataCouchdb) FindAll() ([]model.User, error) {
94 | return []model.User{}, nil
95 | }
96 | func (udc *UserDataCouchdb) FindByName(name string) (*model.User, error) {
97 | return nil, nil
98 | }
99 |
100 | // EnableTx is created to satisfied the txdataservice.TxDataInterface, but it will never be used because NoSQL won't
101 | // support transaction
102 | func (udc *UserDataCouchdb) EnableTx(txFunc func() error) error {
103 | return nil
104 | }
105 |
106 |
--------------------------------------------------------------------------------
/applicationservice/dataservice/userdata/sqldb/userDataSql.go:
--------------------------------------------------------------------------------
1 | // Package sql represents SQL database implementation of the user data persistence layer
2 | package sqldb
3 |
4 | import (
5 | "database/sql"
6 | _ "github.com/go-sql-driver/mysql"
7 | "github.com/jfeng45/gtransaction/gdbc"
8 | "github.com/jfeng45/servicetmpl1/app/logger"
9 | "github.com/jfeng45/servicetmpl1/domain/model"
10 | "github.com/jfeng45/servicetmpl1/tool/timea"
11 | "github.com/pkg/errors"
12 | "time"
13 | )
14 |
15 | const (
16 | // test rollback
17 | //DELETE_USER string = "delete from userinf where username=?"
18 | DELETE_USER string = "delete from userinfo where username=?"
19 | QUERY_USER_BY_ID string = "SELECT * FROM userinfo where uid =?"
20 | QUERY_USER_BY_NAME = "SELECT * FROM userinfo where username =?"
21 | QUERY_USER = "SELECT * FROM userinfo "
22 | UPDATE_USER = "update userinfo set username=?, department=?, created=? where uid=?"
23 | INSERT_USER = "INSERT userinfo SET username=?,department=?,created=?"
24 | )
25 |
26 | // UserDataSql is the SQL implementation of UserDataInterface
27 | type UserDataSql struct {
28 | DB gdbc.SqlGdbc
29 | }
30 |
31 | func (uds *UserDataSql) Remove(username string) (int64, error) {
32 |
33 | stmt, err := uds.DB.Prepare(DELETE_USER)
34 | if err != nil {
35 | return 0, errors.Wrap(err, "")
36 | }
37 | defer stmt.Close()
38 |
39 | res, err := stmt.Exec(username)
40 | if err != nil {
41 | return 0, errors.Wrap(err, "")
42 | }
43 | rowsAffected, err := res.RowsAffected()
44 | if err != nil {
45 | return 0, errors.Wrap(err, "")
46 | }
47 |
48 | logger.Log.Debug("remove:row affected ", rowsAffected)
49 | return rowsAffected, nil
50 | }
51 |
52 | func (uds *UserDataSql) Find(id int) (*model.User, error) {
53 | rows, err := uds.DB.Query(QUERY_USER_BY_ID, id)
54 | if err != nil {
55 | return nil, errors.Wrap(err, "")
56 | }
57 | defer rows.Close()
58 | return retrieveUser(rows)
59 | }
60 | func retrieveUser(rows *sql.Rows) (*model.User, error) {
61 | if rows.Next() {
62 | return rowsToUser(rows)
63 | }
64 | return nil, nil
65 | }
66 | func rowsToUser(rows *sql.Rows) (*model.User, error) {
67 | var ds string
68 | user := &model.User{}
69 | err := rows.Scan(&user.Id, &user.Name, &user.Department, &ds)
70 | if err != nil {
71 | return nil, errors.Wrap(err, "")
72 | }
73 | created, err := time.Parse(timea.FORMAT_ISO8601_DATE, ds)
74 | if err != nil {
75 | return nil, errors.Wrap(err, "")
76 | }
77 | user.Created = created
78 |
79 | logger.Log.Debug("rows to User:", user)
80 | return user, nil
81 | }
82 | func (uds *UserDataSql) FindByName(name string) (*model.User, error) {
83 | //logger.Log.Debug("call FindByName() and name is:", name)
84 | rows, err := uds.DB.Query(QUERY_USER_BY_NAME, name)
85 | if err != nil {
86 | return nil, errors.Wrap(err, "")
87 | }
88 | defer rows.Close()
89 | return retrieveUser(rows)
90 | }
91 |
92 | func (uds *UserDataSql) FindAll() ([]model.User, error) {
93 |
94 | rows, err := uds.DB.Query(QUERY_USER)
95 | if err != nil {
96 | return nil, errors.Wrap(err, "")
97 | }
98 | defer rows.Close()
99 | users := []model.User{}
100 |
101 | //var ds string
102 | for rows.Next() {
103 | user, err := rowsToUser(rows)
104 | if err != nil {
105 | return users, errors.Wrap(err, "")
106 | }
107 | users = append(users, *user)
108 |
109 | }
110 | //need to check error for rows.Next()
111 | if err = rows.Err(); err != nil {
112 | return nil, errors.Wrap(err, "")
113 | }
114 | logger.Log.Debug("find user list:", users)
115 | return users, nil
116 | }
117 |
118 | func (uds *UserDataSql) Update(user *model.User) (int64, error) {
119 |
120 | stmt, err := uds.DB.Prepare(UPDATE_USER)
121 |
122 | if err != nil {
123 | return 0, errors.Wrap(err, "")
124 | }
125 | defer stmt.Close()
126 | res, err := stmt.Exec(user.Name, user.Department, user.Created, user.Id)
127 | if err != nil {
128 | return 0, errors.Wrap(err, "")
129 | }
130 | rowsAffected, err := res.RowsAffected()
131 |
132 | if err != nil {
133 | return 0, errors.Wrap(err, "")
134 | }
135 | logger.Log.Debug("update: rows affected: ", rowsAffected)
136 |
137 | return rowsAffected, nil
138 | }
139 |
140 | func (uds *UserDataSql) Insert(user *model.User) (*model.User, error) {
141 |
142 | stmt, err := uds.DB.Prepare(INSERT_USER)
143 | if err != nil {
144 | return nil, errors.Wrap(err, "")
145 | }
146 | defer stmt.Close()
147 | res, err := stmt.Exec(user.Name, user.Department, user.Created)
148 | if err != nil {
149 | return nil, errors.Wrap(err, "")
150 | }
151 | id, err := res.LastInsertId()
152 | if err != nil {
153 | return nil, errors.Wrap(err, "")
154 | }
155 | user.Id = int(id)
156 | logger.Log.Debug("user inserted:", user)
157 | return user, nil
158 | }
159 |
160 | func (uds *UserDataSql) EnableTx(txFunc func() error) error {
161 | return uds.DB.TxEnd(txFunc)
162 | }
163 |
164 |
--------------------------------------------------------------------------------
/applicationservice/doc.go:
--------------------------------------------------------------------------------
1 | // Package applicationservice represents varies third part resource need to be accessed. Don't be confused with "tool"
2 | // package, which is for third part library as well, but is mainly for lower level library. "applicationservice" package,
3 | // on the other hand, is mainly for data access on business layer
4 | package applicationservice
5 |
--------------------------------------------------------------------------------
/applicationservice/paymentclient/paymentClient.go:
--------------------------------------------------------------------------------
1 | // Package paymentclient is created to show the project structure, no real use.
2 | package paymentclient
3 |
--------------------------------------------------------------------------------
/applicationservice/userclient/generatedclient/usergrpc.pb.go:
--------------------------------------------------------------------------------
1 | // Code generated by protoc-gen-go. DO NOT EDIT.
2 | // source: usergrpc.proto
3 |
4 | package generatedclient
5 |
6 | import proto "github.com/golang/protobuf/proto"
7 | import fmt "fmt"
8 | import math "math"
9 | import timestamp "github.com/golang/protobuf/ptypes/timestamp"
10 |
11 | import (
12 | context "golang.org/x/net/context"
13 | grpc "google.golang.org/grpc"
14 | )
15 |
16 | // Reference imports to suppress errors if they are not otherwise used.
17 | var _ = proto.Marshal
18 | var _ = fmt.Errorf
19 | var _ = math.Inf
20 |
21 | // This is a compile-time assertion to ensure that this generated file
22 | // is compatible with the proto package it is being compiled against.
23 | // A compilation error at this line likely means your copy of the
24 | // proto package needs to be updated.
25 | const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
26 |
27 | type ListUserReq struct {
28 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
29 | XXX_unrecognized []byte `json:"-"`
30 | XXX_sizecache int32 `json:"-"`
31 | }
32 |
33 | func (m *ListUserReq) Reset() { *m = ListUserReq{} }
34 | func (m *ListUserReq) String() string { return proto.CompactTextString(m) }
35 | func (*ListUserReq) ProtoMessage() {}
36 | func (*ListUserReq) Descriptor() ([]byte, []int) {
37 | return fileDescriptor_usergrpc_c653ab406bb3c1d5, []int{0}
38 | }
39 | func (m *ListUserReq) XXX_Unmarshal(b []byte) error {
40 | return xxx_messageInfo_ListUserReq.Unmarshal(m, b)
41 | }
42 | func (m *ListUserReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
43 | return xxx_messageInfo_ListUserReq.Marshal(b, m, deterministic)
44 | }
45 | func (dst *ListUserReq) XXX_Merge(src proto.Message) {
46 | xxx_messageInfo_ListUserReq.Merge(dst, src)
47 | }
48 | func (m *ListUserReq) XXX_Size() int {
49 | return xxx_messageInfo_ListUserReq.Size(m)
50 | }
51 | func (m *ListUserReq) XXX_DiscardUnknown() {
52 | xxx_messageInfo_ListUserReq.DiscardUnknown(m)
53 | }
54 |
55 | var xxx_messageInfo_ListUserReq proto.InternalMessageInfo
56 |
57 | type ListUserResp struct {
58 | User []*User `protobuf:"bytes,1,rep,name=user,proto3" json:"user,omitempty"`
59 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
60 | XXX_unrecognized []byte `json:"-"`
61 | XXX_sizecache int32 `json:"-"`
62 | }
63 |
64 | func (m *ListUserResp) Reset() { *m = ListUserResp{} }
65 | func (m *ListUserResp) String() string { return proto.CompactTextString(m) }
66 | func (*ListUserResp) ProtoMessage() {}
67 | func (*ListUserResp) Descriptor() ([]byte, []int) {
68 | return fileDescriptor_usergrpc_c653ab406bb3c1d5, []int{1}
69 | }
70 | func (m *ListUserResp) XXX_Unmarshal(b []byte) error {
71 | return xxx_messageInfo_ListUserResp.Unmarshal(m, b)
72 | }
73 | func (m *ListUserResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
74 | return xxx_messageInfo_ListUserResp.Marshal(b, m, deterministic)
75 | }
76 | func (dst *ListUserResp) XXX_Merge(src proto.Message) {
77 | xxx_messageInfo_ListUserResp.Merge(dst, src)
78 | }
79 | func (m *ListUserResp) XXX_Size() int {
80 | return xxx_messageInfo_ListUserResp.Size(m)
81 | }
82 | func (m *ListUserResp) XXX_DiscardUnknown() {
83 | xxx_messageInfo_ListUserResp.DiscardUnknown(m)
84 | }
85 |
86 | var xxx_messageInfo_ListUserResp proto.InternalMessageInfo
87 |
88 | func (m *ListUserResp) GetUser() []*User {
89 | if m != nil {
90 | return m.User
91 | }
92 | return nil
93 | }
94 |
95 | type User struct {
96 | Id int32 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
97 | Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
98 | Department string `protobuf:"bytes,3,opt,name=department,proto3" json:"department,omitempty"`
99 | Created *timestamp.Timestamp `protobuf:"bytes,4,opt,name=created,proto3" json:"created,omitempty"`
100 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
101 | XXX_unrecognized []byte `json:"-"`
102 | XXX_sizecache int32 `json:"-"`
103 | }
104 |
105 | func (m *User) Reset() { *m = User{} }
106 | func (m *User) String() string { return proto.CompactTextString(m) }
107 | func (*User) ProtoMessage() {}
108 | func (*User) Descriptor() ([]byte, []int) {
109 | return fileDescriptor_usergrpc_c653ab406bb3c1d5, []int{2}
110 | }
111 | func (m *User) XXX_Unmarshal(b []byte) error {
112 | return xxx_messageInfo_User.Unmarshal(m, b)
113 | }
114 | func (m *User) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
115 | return xxx_messageInfo_User.Marshal(b, m, deterministic)
116 | }
117 | func (dst *User) XXX_Merge(src proto.Message) {
118 | xxx_messageInfo_User.Merge(dst, src)
119 | }
120 | func (m *User) XXX_Size() int {
121 | return xxx_messageInfo_User.Size(m)
122 | }
123 | func (m *User) XXX_DiscardUnknown() {
124 | xxx_messageInfo_User.DiscardUnknown(m)
125 | }
126 |
127 | var xxx_messageInfo_User proto.InternalMessageInfo
128 |
129 | func (m *User) GetId() int32 {
130 | if m != nil {
131 | return m.Id
132 | }
133 | return 0
134 | }
135 |
136 | func (m *User) GetName() string {
137 | if m != nil {
138 | return m.Name
139 | }
140 | return ""
141 | }
142 |
143 | func (m *User) GetDepartment() string {
144 | if m != nil {
145 | return m.Department
146 | }
147 | return ""
148 | }
149 |
150 | func (m *User) GetCreated() *timestamp.Timestamp {
151 | if m != nil {
152 | return m.Created
153 | }
154 | return nil
155 | }
156 |
157 | type RegisterUserReq struct {
158 | User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
159 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
160 | XXX_unrecognized []byte `json:"-"`
161 | XXX_sizecache int32 `json:"-"`
162 | }
163 |
164 | func (m *RegisterUserReq) Reset() { *m = RegisterUserReq{} }
165 | func (m *RegisterUserReq) String() string { return proto.CompactTextString(m) }
166 | func (*RegisterUserReq) ProtoMessage() {}
167 | func (*RegisterUserReq) Descriptor() ([]byte, []int) {
168 | return fileDescriptor_usergrpc_c653ab406bb3c1d5, []int{3}
169 | }
170 | func (m *RegisterUserReq) XXX_Unmarshal(b []byte) error {
171 | return xxx_messageInfo_RegisterUserReq.Unmarshal(m, b)
172 | }
173 | func (m *RegisterUserReq) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
174 | return xxx_messageInfo_RegisterUserReq.Marshal(b, m, deterministic)
175 | }
176 | func (dst *RegisterUserReq) XXX_Merge(src proto.Message) {
177 | xxx_messageInfo_RegisterUserReq.Merge(dst, src)
178 | }
179 | func (m *RegisterUserReq) XXX_Size() int {
180 | return xxx_messageInfo_RegisterUserReq.Size(m)
181 | }
182 | func (m *RegisterUserReq) XXX_DiscardUnknown() {
183 | xxx_messageInfo_RegisterUserReq.DiscardUnknown(m)
184 | }
185 |
186 | var xxx_messageInfo_RegisterUserReq proto.InternalMessageInfo
187 |
188 | func (m *RegisterUserReq) GetUser() *User {
189 | if m != nil {
190 | return m.User
191 | }
192 | return nil
193 | }
194 |
195 | type RegisterUserResp struct {
196 | User *User `protobuf:"bytes,1,opt,name=user,proto3" json:"user,omitempty"`
197 | XXX_NoUnkeyedLiteral struct{} `json:"-"`
198 | XXX_unrecognized []byte `json:"-"`
199 | XXX_sizecache int32 `json:"-"`
200 | }
201 |
202 | func (m *RegisterUserResp) Reset() { *m = RegisterUserResp{} }
203 | func (m *RegisterUserResp) String() string { return proto.CompactTextString(m) }
204 | func (*RegisterUserResp) ProtoMessage() {}
205 | func (*RegisterUserResp) Descriptor() ([]byte, []int) {
206 | return fileDescriptor_usergrpc_c653ab406bb3c1d5, []int{4}
207 | }
208 | func (m *RegisterUserResp) XXX_Unmarshal(b []byte) error {
209 | return xxx_messageInfo_RegisterUserResp.Unmarshal(m, b)
210 | }
211 | func (m *RegisterUserResp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
212 | return xxx_messageInfo_RegisterUserResp.Marshal(b, m, deterministic)
213 | }
214 | func (dst *RegisterUserResp) XXX_Merge(src proto.Message) {
215 | xxx_messageInfo_RegisterUserResp.Merge(dst, src)
216 | }
217 | func (m *RegisterUserResp) XXX_Size() int {
218 | return xxx_messageInfo_RegisterUserResp.Size(m)
219 | }
220 | func (m *RegisterUserResp) XXX_DiscardUnknown() {
221 | xxx_messageInfo_RegisterUserResp.DiscardUnknown(m)
222 | }
223 |
224 | var xxx_messageInfo_RegisterUserResp proto.InternalMessageInfo
225 |
226 | func (m *RegisterUserResp) GetUser() *User {
227 | if m != nil {
228 | return m.User
229 | }
230 | return nil
231 | }
232 |
233 | func init() {
234 | proto.RegisterType((*ListUserReq)(nil), "generatedclient.ListUserReq")
235 | proto.RegisterType((*ListUserResp)(nil), "generatedclient.ListUserResp")
236 | proto.RegisterType((*User)(nil), "generatedclient.User")
237 | proto.RegisterType((*RegisterUserReq)(nil), "generatedclient.RegisterUserReq")
238 | proto.RegisterType((*RegisterUserResp)(nil), "generatedclient.RegisterUserResp")
239 | }
240 |
241 | // Reference imports to suppress errors if they are not otherwise used.
242 | var _ context.Context
243 | var _ grpc.ClientConn
244 |
245 | // This is a compile-time assertion to ensure that this generated file
246 | // is compatible with the grpc package it is being compiled against.
247 | const _ = grpc.SupportPackageIsVersion4
248 |
249 | // UserServiceClient is the client API for UserService service.
250 | //
251 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
252 | type UserServiceClient interface {
253 | RegisterUser(ctx context.Context, in *RegisterUserReq, opts ...grpc.CallOption) (*RegisterUserResp, error)
254 | ListUser(ctx context.Context, in *ListUserReq, opts ...grpc.CallOption) (*ListUserResp, error)
255 | }
256 |
257 | type userServiceClient struct {
258 | cc *grpc.ClientConn
259 | }
260 |
261 | func NewUserServiceClient(cc *grpc.ClientConn) UserServiceClient {
262 | return &userServiceClient{cc}
263 | }
264 |
265 | func (c *userServiceClient) RegisterUser(ctx context.Context, in *RegisterUserReq, opts ...grpc.CallOption) (*RegisterUserResp, error) {
266 | out := new(RegisterUserResp)
267 | err := c.cc.Invoke(ctx, "/generatedclient.UserService/RegisterUser", in, out, opts...)
268 | if err != nil {
269 | return nil, err
270 | }
271 | return out, nil
272 | }
273 |
274 | func (c *userServiceClient) ListUser(ctx context.Context, in *ListUserReq, opts ...grpc.CallOption) (*ListUserResp, error) {
275 | out := new(ListUserResp)
276 | err := c.cc.Invoke(ctx, "/generatedclient.UserService/ListUser", in, out, opts...)
277 | if err != nil {
278 | return nil, err
279 | }
280 | return out, nil
281 | }
282 |
283 | // UserServiceServer is the server API for UserService service.
284 | type UserServiceServer interface {
285 | RegisterUser(context.Context, *RegisterUserReq) (*RegisterUserResp, error)
286 | ListUser(context.Context, *ListUserReq) (*ListUserResp, error)
287 | }
288 |
289 | func RegisterUserServiceServer(s *grpc.Server, srv UserServiceServer) {
290 | s.RegisterService(&_UserService_serviceDesc, srv)
291 | }
292 |
293 | func _UserService_RegisterUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
294 | in := new(RegisterUserReq)
295 | if err := dec(in); err != nil {
296 | return nil, err
297 | }
298 | if interceptor == nil {
299 | return srv.(UserServiceServer).RegisterUser(ctx, in)
300 | }
301 | info := &grpc.UnaryServerInfo{
302 | Server: srv,
303 | FullMethod: "/generatedclient.UserService/RegisterUser",
304 | }
305 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
306 | return srv.(UserServiceServer).RegisterUser(ctx, req.(*RegisterUserReq))
307 | }
308 | return interceptor(ctx, in, info, handler)
309 | }
310 |
311 | func _UserService_ListUser_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
312 | in := new(ListUserReq)
313 | if err := dec(in); err != nil {
314 | return nil, err
315 | }
316 | if interceptor == nil {
317 | return srv.(UserServiceServer).ListUser(ctx, in)
318 | }
319 | info := &grpc.UnaryServerInfo{
320 | Server: srv,
321 | FullMethod: "/generatedclient.UserService/ListUser",
322 | }
323 | handler := func(ctx context.Context, req interface{}) (interface{}, error) {
324 | return srv.(UserServiceServer).ListUser(ctx, req.(*ListUserReq))
325 | }
326 | return interceptor(ctx, in, info, handler)
327 | }
328 |
329 | var _UserService_serviceDesc = grpc.ServiceDesc{
330 | ServiceName: "generatedclient.UserService",
331 | HandlerType: (*UserServiceServer)(nil),
332 | Methods: []grpc.MethodDesc{
333 | {
334 | MethodName: "RegisterUser",
335 | Handler: _UserService_RegisterUser_Handler,
336 | },
337 | {
338 | MethodName: "ListUser",
339 | Handler: _UserService_ListUser_Handler,
340 | },
341 | },
342 | Streams: []grpc.StreamDesc{},
343 | Metadata: "usergrpc.proto",
344 | }
345 |
346 | func init() { proto.RegisterFile("usergrpc.proto", fileDescriptor_usergrpc_c653ab406bb3c1d5) }
347 |
348 | var fileDescriptor_usergrpc_c653ab406bb3c1d5 = []byte{
349 | // 295 bytes of a gzipped FileDescriptorProto
350 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x91, 0x4d, 0x4e, 0xc3, 0x30,
351 | 0x10, 0x85, 0x71, 0x1a, 0xfe, 0x26, 0xa5, 0x45, 0x23, 0x21, 0x45, 0x11, 0x3f, 0x21, 0xab, 0xb0,
352 | 0x71, 0xa5, 0xc0, 0x06, 0x09, 0x0e, 0x80, 0xc4, 0x2a, 0xd0, 0x03, 0xa4, 0xc9, 0x10, 0x59, 0x6a,
353 | 0x12, 0x63, 0xbb, 0xac, 0xb9, 0x11, 0x57, 0x44, 0x71, 0x89, 0x48, 0x5b, 0xa9, 0xd9, 0x79, 0x3c,
354 | 0xef, 0xbd, 0xf1, 0xe7, 0x81, 0xc9, 0x4a, 0x93, 0x2a, 0x95, 0xcc, 0xb9, 0x54, 0x8d, 0x69, 0x70,
355 | 0x5a, 0x52, 0x4d, 0x2a, 0x33, 0x54, 0xe4, 0x4b, 0x41, 0xb5, 0x09, 0x6e, 0xca, 0xa6, 0x29, 0x97,
356 | 0x34, 0xb3, 0xed, 0xc5, 0xea, 0x63, 0x66, 0x44, 0x45, 0xda, 0x64, 0x95, 0x5c, 0x3b, 0xa2, 0x33,
357 | 0xf0, 0x5e, 0x85, 0x36, 0x73, 0x4d, 0x2a, 0xa5, 0xcf, 0xe8, 0x11, 0xc6, 0xff, 0xa5, 0x96, 0x78,
358 | 0x07, 0x6e, 0x3b, 0xc2, 0x67, 0xe1, 0x28, 0xf6, 0x92, 0x0b, 0xbe, 0x95, 0xcf, 0xad, 0xd0, 0x4a,
359 | 0xa2, 0x6f, 0x06, 0x6e, 0x5b, 0xe2, 0x04, 0x1c, 0x51, 0xf8, 0x2c, 0x64, 0xf1, 0x61, 0xea, 0x88,
360 | 0x02, 0x11, 0xdc, 0x3a, 0xab, 0xc8, 0x77, 0x42, 0x16, 0x9f, 0xa6, 0xf6, 0x8c, 0xd7, 0x00, 0x05,
361 | 0xc9, 0x4c, 0x99, 0x8a, 0x6a, 0xe3, 0x8f, 0x6c, 0xa7, 0x77, 0x83, 0x0f, 0x70, 0x9c, 0x2b, 0x6a,
362 | 0x07, 0xf9, 0x6e, 0xc8, 0x62, 0x2f, 0x09, 0xf8, 0x9a, 0x84, 0x77, 0x24, 0xfc, 0xbd, 0x23, 0x49,
363 | 0x3b, 0x69, 0xf4, 0x04, 0xd3, 0x94, 0x4a, 0xa1, 0x0d, 0xa9, 0x3f, 0xa0, 0x1e, 0x00, 0x1b, 0x02,
364 | 0x78, 0x86, 0xf3, 0x4d, 0xf7, 0x06, 0xff, 0x90, 0x3d, 0xf9, 0x61, 0xe0, 0xb5, 0xe5, 0x1b, 0xa9,
365 | 0x2f, 0x91, 0x13, 0xce, 0x61, 0xdc, 0x8f, 0xc3, 0x70, 0xc7, 0xbc, 0xf5, 0xd6, 0xe0, 0x76, 0x40,
366 | 0xa1, 0x65, 0x74, 0x80, 0x2f, 0x70, 0xd2, 0x6d, 0x08, 0x2f, 0x77, 0x0c, 0xbd, 0x5d, 0x06, 0x57,
367 | 0x7b, 0xba, 0x6d, 0xd4, 0xe2, 0xc8, 0xfe, 0xe5, 0xfd, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xd3,
368 | 0x04, 0xca, 0xf9, 0x46, 0x02, 0x00, 0x00,
369 | }
370 |
--------------------------------------------------------------------------------
/applicationservice/userclient/generatedclient/usergrpc.proto:
--------------------------------------------------------------------------------
1 | syntax = "proto3";
2 | package generatedclient;
3 |
4 | import "google/protobuf/timestamp.proto";
5 |
6 | service UserService {
7 | rpc RegisterUser(RegisterUserReq ) returns (RegisterUserResp) {}
8 | rpc ListUser(ListUserReq) returns (ListUserResp) {}
9 | }
10 |
11 | message ListUserReq {
12 | }
13 | message ListUserResp {
14 | repeated User user =1;
15 | }
16 |
17 | message User {
18 | int32 id = 1;
19 | string name = 2;
20 | string department =3;
21 | google.protobuf.Timestamp created =4;
22 | }
23 | message RegisterUserReq {
24 | User user = 1;
25 | }
26 |
27 | message RegisterUserResp {
28 | User user =1;
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/applicationservice/userclient/userGrpc.go:
--------------------------------------------------------------------------------
1 | // Package userclient is client library if you need to call the user Micro-service as a client.
2 | // It provides client library and the data transformation service.
3 | package userclient
4 |
5 | import (
6 | "github.com/golang/protobuf/ptypes"
7 | "github.com/jfeng45/servicetmpl1/applicationservice/userclient/generatedclient"
8 | "github.com/jfeng45/servicetmpl1/domain/model"
9 | "github.com/pkg/errors"
10 | )
11 |
12 | // GrpcToUser converts from grpc User type to domain Model user type
13 | func GrpcToUser(user *generatedclient.User) (*model.User, error) {
14 | if user == nil {
15 | return nil, nil
16 | }
17 | resultUser := model.User{}
18 |
19 | resultUser.Id = int(user.Id)
20 | resultUser.Name = user.Name
21 | resultUser.Department = user.Department
22 | created, err := ptypes.Timestamp(user.Created)
23 | if err != nil {
24 | return nil, errors.Wrap(err, "")
25 | }
26 | resultUser.Created = created
27 | return &resultUser, nil
28 | }
29 |
30 | // UserToGrpc converts from domain Model User type to grpc user type
31 | func UserToGrpc(user *model.User) (*generatedclient.User, error) {
32 | if user == nil {
33 | return nil, nil
34 | }
35 | resultUser := generatedclient.User{}
36 | resultUser.Id = int32(user.Id)
37 | resultUser.Name = user.Name
38 | resultUser.Department = user.Department
39 | created, err := ptypes.TimestampProto(user.Created)
40 | if err != nil {
41 | return nil, errors.Wrap(err, "")
42 | }
43 | resultUser.Created = created
44 | return &resultUser, nil
45 | }
46 |
47 | // UserListToGrpc converts from array of domain Model User type to array of grpc user type
48 | func UserListToGrpc(ul []model.User) ([]*generatedclient.User, error) {
49 | var gul []*generatedclient.User
50 | for _, user := range ul {
51 | gu, err := UserToGrpc(&user)
52 | if err != nil {
53 | return nil, errors.Wrap(err, "")
54 | }
55 | gul = append(gul, gu)
56 | }
57 | return gul, nil
58 | }
59 |
--------------------------------------------------------------------------------
/cmd/grpcclient/grpcClientMain.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/golang/protobuf/ptypes"
7 | uspb "github.com/jfeng45/servicetmpl1/applicationservice/userclient/generatedclient"
8 | "golang.org/x/net/context"
9 | "google.golang.org/grpc"
10 | )
11 |
12 | const (
13 | TARGET string = "localhost:5052"
14 | )
15 |
16 | func callRegisterUser(usc uspb.UserServiceClient) {
17 | ctx := context.Background()
18 |
19 | created := ptypes.TimestampNow()
20 | u := uspb.User{Name: "Tony", Department: "IT", Created: created}
21 |
22 | resp, err := usc.RegisterUser(ctx, &uspb.RegisterUserReq{User: &u})
23 |
24 | if err != nil {
25 | fmt.Println(err)
26 | } else {
27 | fmt.Println("results user: ", resp.User)
28 | }
29 | }
30 | func callListUser(usc uspb.UserServiceClient) {
31 |
32 | resp, err := usc.ListUser(context.Background(), &uspb.ListUserReq{})
33 | if err != nil {
34 | fmt.Println(err)
35 | } else {
36 | fmt.Printf("Got list users %s\n", resp.User)
37 | }
38 | }
39 |
40 | func main() {
41 |
42 | conn, err := grpc.Dial(TARGET, grpc.WithInsecure())
43 | if err != nil {
44 | fmt.Errorf("failed to dial server: %v", err)
45 | }
46 | userServiceClient := uspb.NewUserServiceClient(conn)
47 | fmt.Println("client strated")
48 |
49 | callRegisterUser(userServiceClient)
50 | callListUser(userServiceClient)
51 | }
52 |
--------------------------------------------------------------------------------
/cmd/grpcserver/grpcServerMain.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "net"
7 |
8 | "github.com/jfeng45/servicetmpl1/app"
9 | "github.com/jfeng45/servicetmpl1/app/container/containerhelper"
10 | "github.com/jfeng45/servicetmpl1/app/container/servicecontainer"
11 | "github.com/jfeng45/servicetmpl1/app/logger"
12 | "github.com/jfeng45/servicetmpl1/applicationservice/userclient"
13 | uspb "github.com/jfeng45/servicetmpl1/applicationservice/userclient/generatedclient"
14 | "github.com/pkg/errors"
15 | "google.golang.org/grpc"
16 | )
17 |
18 | const (
19 | DEV_CONFIG string = "../../app/config/appConfigDev.yaml"
20 | PROD_CONFIG string = "../../app/config/appConfigProd.yaml"
21 | )
22 |
23 | type UserService struct {
24 | //container container.Container
25 | container *servicecontainer.ServiceContainer
26 | }
27 |
28 | func catchPanic() {
29 | if p := recover(); p != nil {
30 | logger.Log.Errorf("%+v\n", p)
31 | }
32 | }
33 |
34 | func (uss *UserService) RegisterUser(ctx context.Context, req *uspb.RegisterUserReq) (*uspb.RegisterUserResp, error) {
35 | defer catchPanic()
36 | logger.Log.Debug("RegisterUser called")
37 |
38 | ruci, err := containerhelper.GetRegistrationUseCase(uss.container)
39 | if err != nil {
40 | logger.Log.Errorf("%+v\n", err)
41 | return nil, errors.Wrap(err, "")
42 | }
43 | mu, err := userclient.GrpcToUser(req.User)
44 |
45 | if err != nil {
46 | logger.Log.Errorf("%+v\n", err)
47 | return nil, errors.Wrap(err, "")
48 | }
49 | logger.Log.Debug("mu:", mu)
50 | resultUser, err := ruci.RegisterUser(mu)
51 | if err != nil {
52 | logger.Log.Errorf("%+v\n", err)
53 | return nil, errors.Wrap(err, "")
54 | }
55 | logger.Log.Debug("resultUser:", resultUser)
56 | gu, err := userclient.UserToGrpc(resultUser)
57 | if err != nil {
58 | logger.Log.Errorf("%+v\n", err)
59 | return nil, errors.Wrap(err, "")
60 | }
61 |
62 | logger.Log.Debug("user registered: ", gu)
63 |
64 | return &uspb.RegisterUserResp{User: gu}, nil
65 |
66 | }
67 |
68 | func (uss *UserService) ListUser(ctx context.Context, in *uspb.ListUserReq) (*uspb.ListUserResp, error) {
69 | defer catchPanic()
70 | logger.Log.Debug("ListUser called")
71 |
72 | luci, err := containerhelper.GetListUserUseCase(uss.container)
73 | if err != nil {
74 | logger.Log.Errorf("%+v\n", err)
75 | return nil, errors.Wrap(err, "")
76 | }
77 |
78 | lu, err := luci.ListUser()
79 | if err != nil {
80 | logger.Log.Errorf("%+v\n", err)
81 | return nil, errors.Wrap(err, "")
82 | }
83 | gu, err := userclient.UserListToGrpc(lu)
84 | if err != nil {
85 | logger.Log.Errorf("%+v\n", err)
86 | return nil, errors.Wrap(err, "")
87 | }
88 |
89 | logger.Log.Debug("user list: ", gu)
90 |
91 | return &uspb.ListUserResp{User: gu}, nil
92 |
93 | }
94 | func runServer(sc *servicecontainer.ServiceContainer) error {
95 | logger.Log.Debug("start runserver")
96 |
97 | srv := grpc.NewServer()
98 |
99 | cs := &UserService{sc}
100 | uspb.RegisterUserServiceServer(srv, cs)
101 | //l, err:=net.Listen(GRPC_NETWORK, GRPC_ADDRESS)
102 | ugc := sc.AppConfig.UserGrpcConfig
103 | logger.Log.Debugf("userGrpcConfig: %+v\n", ugc)
104 | l, err := net.Listen(ugc.DriverName, ugc.UrlAddress)
105 | if err != nil {
106 | return errors.Wrap(err, "")
107 | } else {
108 | logger.Log.Debug("server listening")
109 | }
110 | return srv.Serve(l)
111 | }
112 |
113 | func main() {
114 | filename := DEV_CONFIG
115 | //filename := PROD_CONFIG
116 | container, err := buildContainer(filename)
117 | if err != nil {
118 | fmt.Printf("%+v\n", err)
119 | //logger.Log.Errorf("%+v\n", err)
120 | panic(err)
121 | }
122 | if err := runServer(container); err != nil {
123 | logger.Log.Errorf("Failed to run user server: %+v\n", err)
124 | panic(err)
125 | } else {
126 | logger.Log.Info("server started")
127 | }
128 | }
129 |
130 | func buildContainer(filename string) (*servicecontainer.ServiceContainer, error) {
131 |
132 | container, err := app.InitApp(filename)
133 | sc := container.(*servicecontainer.ServiceContainer)
134 | if err != nil {
135 | //logger.Log.Errorf("%+v\n", err)
136 | return nil, errors.Wrap(err, "")
137 | }
138 | return sc, nil
139 | }
140 |
--------------------------------------------------------------------------------
/cmd/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/jfeng45/servicetmpl1/app"
6 | "github.com/jfeng45/servicetmpl1/app/container"
7 | "github.com/jfeng45/servicetmpl1/app/container/containerhelper"
8 | "github.com/jfeng45/servicetmpl1/app/logger"
9 | "github.com/jfeng45/servicetmpl1/domain/model"
10 | "github.com/jfeng45/servicetmpl1/tool/timea"
11 | "github.com/pkg/errors"
12 | "time"
13 | )
14 |
15 | const (
16 | DEV_CONFIG string = "../app/config/appConfigDev.yaml"
17 | PROD_CONFIG string = "../app/config/appConfigProd.yaml"
18 | )
19 |
20 | func main() {
21 | testMySql()
22 | //testCouchDB()
23 | }
24 |
25 | func testMySql() {
26 |
27 | filename := DEV_CONFIG
28 | container, err := buildContainer(filename)
29 | if err != nil {
30 | fmt.Printf("%+v\n", err)
31 | return
32 | }
33 | testListUser(container)
34 | testFindById(container)
35 | testRegisterUser(container)
36 | testModifyUser(container)
37 | testUnregister(container)
38 | testModifyAndUnregister(container)
39 | testModifyAndUnregisterWithTx(container)
40 |
41 | }
42 | func testCouchDB() {
43 | filename := PROD_CONFIG
44 | container, err := buildContainer(filename)
45 | if err != nil {
46 | fmt.Printf("%+v\n", err)
47 | //logger.Log.Errorf("%+v\n", err)
48 | return
49 | }
50 | testFindById(container)
51 | }
52 |
53 | func testUnregister(container container.Container) {
54 |
55 | ruci, err := containerhelper.GetRegistrationUseCase(container)
56 | if err != nil {
57 | logger.Log.Fatal("registration interface build failed:%+v\n", err)
58 | }
59 | username := "Aditi"
60 | err = ruci.UnregisterUser(username)
61 | if err != nil {
62 | logger.Log.Fatalf("testUnregister failed:%+v\n", err)
63 | }
64 | logger.Log.Infof("testUnregister successully")
65 | }
66 |
67 | func testRegisterUser(container container.Container) {
68 | ruci, err := containerhelper.GetRegistrationUseCase(container)
69 | if err != nil {
70 | logger.Log.Fatal("registration interface build failed:%+v\n", err)
71 | }
72 | created, err := time.Parse(timea.FORMAT_ISO8601_DATE, "2018-12-09")
73 | if err != nil {
74 | logger.Log.Errorf("date format err:%+v\n", err)
75 | }
76 |
77 | user := model.User{Name: "Brian", Department: "Marketing", Created: created}
78 |
79 | resultUser, err := ruci.RegisterUser(&user)
80 | if err != nil {
81 | logger.Log.Errorf("user registration failed:%+v\n", err)
82 | } else {
83 | logger.Log.Info("new user registered:", resultUser)
84 | }
85 | }
86 |
87 | func testModifyUser(container container.Container) {
88 | ruci, err := containerhelper.GetRegistrationUseCase(container)
89 | if err != nil {
90 | logger.Log.Fatal("registration interface build failed:%+v\n", err)
91 | }
92 | created, err := time.Parse(timea.FORMAT_ISO8601_DATE, "2019-12-01")
93 | if err != nil {
94 | logger.Log.Errorf("date format err:%+v\n", err)
95 | }
96 | user := model.User{Id: 29, Name: "Aditi", Department: "HR", Created: created}
97 | err = ruci.ModifyUser(&user)
98 | if err != nil {
99 | logger.Log.Infof("Modify user failed:%+v\n", err)
100 | } else {
101 | logger.Log.Info("user modified succeed:", user)
102 | }
103 | }
104 |
105 | func testListUser(container container.Container) {
106 | rluf, err := containerhelper.GetListUserUseCase(container)
107 | if err != nil {
108 | logger.Log.Fatal("RetrieveListUser interface build failed:", err)
109 | }
110 | users, err := rluf.ListUser()
111 | if err != nil {
112 | logger.Log.Errorf("user list failed:%+v\n", err)
113 | }
114 | logger.Log.Info("user list:", users)
115 | }
116 |
117 | func testModifyAndUnregister(container container.Container) {
118 | ruci, err := containerhelper.GetRegistrationUseCase(container)
119 | if err != nil {
120 | logger.Log.Fatal("RegisterRegistration interface build failed:%+v\n", err)
121 | }
122 | created, err := time.Parse(timea.FORMAT_ISO8601_DATE, "2018-12-09")
123 | if err != nil {
124 | logger.Log.Errorf("date format err:%+v\n", err)
125 | }
126 | user := model.User{Id: 31, Name: "Richard", Department: "Sales", Created: created}
127 | err = ruci.ModifyAndUnregister(&user)
128 | if err != nil {
129 | logger.Log.Errorf("ModifyAndUnregister failed:%+v\n", err)
130 | } else {
131 | logger.Log.Infof("ModifyAndUnregister succeed")
132 | }
133 | }
134 |
135 | func testModifyAndUnregisterWithTx(container container.Container) {
136 | rtuci, err := containerhelper.GetRegistrationTxUseCase(container)
137 | if err != nil {
138 | logger.Log.Fatal("RegisterRegistration interface build failed:%+v\n", err)
139 | }
140 | created, err := time.Parse(timea.FORMAT_ISO8601_DATE, "2018-12-09")
141 | if err != nil {
142 | logger.Log.Errorf("date format err:%+v\n", err)
143 | }
144 | user := model.User{Id: 32, Name: "Richard", Department: "Finance", Created: created}
145 | err = rtuci.ModifyAndUnregisterWithTx(&user)
146 | if err != nil {
147 | logger.Log.Errorf("ModifyAndUnregisterWithTx failed:%+v\n", err)
148 | } else {
149 | logger.Log.Infof("ModifyAndUnregisterWithTx succeed")
150 | }
151 | }
152 |
153 | func testFindById(container container.Container) {
154 | //It is uid in database. Make sure you have it in database, otherwise it won't find it.
155 | id := 10
156 | rluf, err := containerhelper.GetListUserUseCase(container)
157 | if err != nil {
158 | logger.Log.Fatalf("RetrieveListUser interface build failed:%+v\n", err)
159 | }
160 | user, err := rluf.Find(id)
161 | if err != nil {
162 | logger.Log.Errorf("fin user failed failed:%+v\n", err)
163 | }
164 | logger.Log.Info("find user:", user)
165 | }
166 |
167 | func buildContainer(filename string) (container.Container, error) {
168 | container, err := app.InitApp(filename)
169 | if err != nil {
170 | return nil, errors.Wrap(err, "")
171 | }
172 | return container, nil
173 | }
174 |
--------------------------------------------------------------------------------
/domain/model/cache.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | //Cache is the domain model for cache service
4 | type Cache struct {
5 | key string
6 | value []byte
7 | }
8 |
--------------------------------------------------------------------------------
/domain/model/course.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | // Course is created to show the project layout and courseDataServiceFactory, no real use.
4 | type Course struct {
5 | Id int
6 | Name string
7 | }
8 |
--------------------------------------------------------------------------------
/domain/model/user.go:
--------------------------------------------------------------------------------
1 | // Package model represents domain model. Every domain model type should have it's own file.
2 | // It shouldn't depends on any other package in the application.
3 | // It should only has domain model type and limited domain logic, in this example, validation logic. Because all other
4 | // package depends on this package, the import of this package should be as small as possible.
5 |
6 | package model
7 |
8 | import (
9 | "github.com/go-ozzo/ozzo-validation"
10 | "time"
11 | )
12 |
13 | // User has a name, department and created date. Name and created are required, department is optional.
14 | // Id is auto-generated by database after the user is persisted.
15 | // json is for couchdb
16 | type User struct {
17 | Id int `json:"uid"`
18 | Name string `json:"username"`
19 | Department string `json:"department"`
20 | Created time.Time `json:"created"`
21 | }
22 |
23 | // Validate validates a newly created user, which has not persisted to database yet, so Id is empty
24 | func (u User) Validate() error {
25 | return validation.ValidateStruct(&u,
26 | validation.Field(&u.Name, validation.Required),
27 | validation.Field(&u.Created, validation.Required))
28 | }
29 |
30 | //ValidatePersisted validate a user that has been persisted to database, basically Id is not empty
31 | func (u User) ValidatePersisted() error {
32 | return validation.ValidateStruct(&u,
33 | validation.Field(&u.Id, validation.Required),
34 | validation.Field(&u.Name, validation.Required),
35 | validation.Field(&u.Created, validation.Required))
36 | }
37 |
--------------------------------------------------------------------------------
/domain/usecase/listuser/listUser.go:
--------------------------------------------------------------------------------
1 | // Package registration represents the concrete implementation of ListUserUseCaseInterface interface
2 | package listuser
3 |
4 | import (
5 | "github.com/jfeng45/servicetmpl1/app/logger"
6 | "github.com/jfeng45/servicetmpl1/applicationservice/dataservice"
7 | "github.com/jfeng45/servicetmpl1/domain/model"
8 | "github.com/pkg/errors"
9 | "strconv"
10 | )
11 |
12 | // ListUserUseCase implements ListUseCaseInterface.
13 | type ListUserUseCase struct {
14 | // UserDataInterface, which is a interface to underline database connection and can be used to access
15 | // persistence layer
16 | UserDataInterface dataservice.UserDataInterface
17 | // CacheDataInterface, which is a interface to outside gRPC cache service and can be used to access gRPC service
18 | CacheDataInterface dataservice.CacheDataInterface
19 | }
20 |
21 | func (luc *ListUserUseCase) ListUser() ([]model.User, error) {
22 | return luc.UserDataInterface.FindAll()
23 | }
24 | func (luc *ListUserUseCase) Find(id int) (*model.User, error) {
25 | users, err := luc.getFromCache(strconv.Itoa(id))
26 | if err != nil {
27 | //not found in cache and continue
28 | logger.Log.Errorf("get from cache error:", err)
29 | //return nil, errors.Wrap(err, "")
30 | }
31 | if users != nil {
32 | //here should return the results from cache, however, right now the cache doesn't store user info,
33 | //so, just call find(id). This is not real code. Please replace it with real code
34 | return luc.UserDataInterface.Find(id)
35 | }
36 | return luc.UserDataInterface.Find(id)
37 | }
38 |
39 | // GetFromCache is a fake function to just show a call to outside service, the call to outside is working,
40 | // but the results returned is not right
41 | func (luc *ListUserUseCase) getFromCache(key string) ([]model.User, error) {
42 | value, err := luc.CacheDataInterface.Get(key)
43 | if err != nil {
44 | return nil, errors.Wrap(err, "")
45 | }
46 | logger.Log.Info("value from get cache: ", value)
47 | return nil, nil
48 | }
49 |
--------------------------------------------------------------------------------
/domain/usecase/registration/registrationHelper.go:
--------------------------------------------------------------------------------
1 | package registration
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/applicationservice/dataservice"
5 | "github.com/jfeng45/servicetmpl1/domain/model"
6 | "github.com/pkg/errors"
7 | "strconv"
8 | )
9 |
10 | func modifyUser(udi dataservice.UserDataInterface, user *model.User) error {
11 | //loggera.Log.Debug("modifyUser")
12 | err := user.ValidatePersisted()
13 | if err != nil {
14 | return errors.Wrap(err, "user validation failed")
15 | }
16 | rowsAffected, err := udi.Update(user)
17 | if err != nil {
18 | return errors.Wrap(err, "")
19 | }
20 | if rowsAffected != 1 {
21 | return errors.New("Modify user failed. rows affected is " + strconv.Itoa(int(rowsAffected)))
22 | }
23 | return nil
24 | }
25 |
26 | func unregisterUser(udi dataservice.UserDataInterface, username string) error {
27 | affected, err := udi.Remove(username)
28 | if err != nil {
29 | return errors.Wrap(err, "")
30 | }
31 | if affected == 0 {
32 | errStr := "UnregisterUser failed. No such user " + username
33 | return errors.New(errStr)
34 | }
35 |
36 | if affected != 1 {
37 | errStr := "UnregisterUser failed. Number of users unregistered are " + strconv.Itoa(int(affected))
38 | return errors.New(errStr)
39 | }
40 | return nil
41 | }
42 |
43 | // The business function will be wrapped inside a transaction or a non-transaction function
44 | // It needs to be written in a way that every error will be returned so it can be caught by TxEnd() function,
45 | // which will handle commit and rollback
46 | func ModifyAndUnregister(udi dataservice.UserDataInterface, user *model.User) error {
47 | //loggera.Log.Debug("ModifyAndUnregister")
48 | err := modifyUser(udi, user)
49 | if err != nil {
50 | return errors.Wrap(err, "")
51 | }
52 | err = unregisterUser(udi, user.Name)
53 | if err != nil {
54 | return errors.Wrap(err, "")
55 | }
56 | return nil
57 | }
58 |
--------------------------------------------------------------------------------
/domain/usecase/registration/registraton.go:
--------------------------------------------------------------------------------
1 | // Package registrationTx represents the concrete implementation of RegistrationUseCaseInterface and
2 | // RegistrationTxUseCaseInterface interface.
3 | // Because the same business function can be created to support both transaction and non-transaction,
4 | // a shared business function is created in a helper file, then we can wrap that function with transaction
5 | // or non-transaction.
6 | package registration
7 |
8 | import (
9 | "github.com/jfeng45/servicetmpl1/applicationservice/dataservice"
10 | "github.com/jfeng45/servicetmpl1/domain/model"
11 | "github.com/pkg/errors"
12 | )
13 |
14 | // RegistrationUseCase implements RegistrationUseCaseInterface.
15 | // It has UserDataInterface, which can be used to access persistence layer
16 | // TxDataInterface is needed to support transaction
17 | type RegistrationUseCase struct {
18 | UserDataInterface dataservice.UserDataInterface
19 | }
20 |
21 | func (ruc *RegistrationUseCase) RegisterUser(user *model.User) (*model.User, error) {
22 | err := user.Validate()
23 | if err != nil {
24 | return nil, errors.Wrap(err, "user validation failed")
25 | }
26 | isDup, err := ruc.isDuplicate(user.Name)
27 | if err != nil {
28 | return nil, errors.Wrap(err, "")
29 | }
30 | if isDup {
31 | return nil, errors.New("duplicate user for " + user.Name)
32 | }
33 | resultUser, err := ruc.UserDataInterface.Insert(user)
34 |
35 | if err != nil {
36 | return nil, errors.Wrap(err, "")
37 | }
38 | return resultUser, nil
39 | }
40 |
41 | func (ruc *RegistrationUseCase) ModifyUser(user *model.User) error {
42 | return modifyUser(ruc.UserDataInterface, user)
43 | }
44 |
45 | func (ruc *RegistrationUseCase) isDuplicate(name string) (bool, error) {
46 | user, err := ruc.UserDataInterface.FindByName(name)
47 | //logger.Log.Debug("isDuplicate() user:", user)
48 | if err != nil {
49 | return false, errors.Wrap(err, "")
50 | }
51 | if user != nil {
52 | return true, nil
53 | }
54 | return false, nil
55 | }
56 |
57 | func (ruc *RegistrationUseCase) UnregisterUser(username string) error {
58 | return unregisterUser(ruc.UserDataInterface, username)
59 | }
60 |
61 | // The use case of ModifyAndUnregister without transaction
62 | func (ruc *RegistrationUseCase) ModifyAndUnregister(user *model.User) error {
63 | return ModifyAndUnregister(ruc.UserDataInterface, user)
64 | }
65 |
66 |
--------------------------------------------------------------------------------
/domain/usecase/registration/registratonTx.go:
--------------------------------------------------------------------------------
1 | package registration
2 |
3 | import (
4 | "github.com/jfeng45/servicetmpl1/applicationservice/dataservice"
5 | "github.com/jfeng45/servicetmpl1/domain/model"
6 | )
7 |
8 | // RegistrationTxUseCase implements RegistrationTxUseCaseInterface.
9 | // It has UserDataInterface, which can be used to access persistence layer
10 | type RegistrationTxUseCase struct {
11 | UserDataInterface dataservice.UserDataInterface
12 | }
13 |
14 | // The use case of ModifyAndUnregister with transaction
15 | func (rtuc *RegistrationTxUseCase) ModifyAndUnregisterWithTx(user *model.User) error {
16 |
17 | udi := rtuc.UserDataInterface
18 | return udi.EnableTx(func() error {
19 | // wrap the business function inside the TxEnd function
20 | return ModifyAndUnregister(udi, user)
21 | })
22 | }
23 |
--------------------------------------------------------------------------------
/domain/usecase/useCase.go:
--------------------------------------------------------------------------------
1 | // Package usecase defines all the interfaces for a Micro-service application.
2 | // It is the entry point for the application's business logic. It is a top level package for a Micro-service application.
3 | // This top level package only defines interface, the concrete implementations are defined in sub-package of it.
4 | // It only depends on model package. No other package should dependent on it except cmd.
5 |
6 | // If transaction is supported, the transaction boundary should be defined in this package.
7 | // A suffix-"WithTx" can be added to the name of a transaction function to distinguish it from a non-transaction one.
8 |
9 | // The use cases in this application are not real world use cases, and they are created to illustrate the project layout.
10 | // You should replace them with your real use case.
11 |
12 | package usecase
13 |
14 | import (
15 | "github.com/jfeng45/servicetmpl1/domain/model"
16 | )
17 |
18 | // RegistrationUseCaseInterface is for users to register themselves to an application. It has registration related functions.
19 | // ModifyAndUnregisterWithTx() is the one supporting transaction, the other are not.
20 | type RegistrationUseCaseInterface interface {
21 | // RegisterUser register a user to an application, basically save it to a database. The returned resultUser that has
22 | // a Id ( auto generated by database) after persisted
23 | RegisterUser(user *model.User) (resultUser *model.User, err error)
24 | // UnregisterUser unregister a user from an application by user name, basically removing it from a database.
25 | UnregisterUser(username string) error
26 | // ModifyUser change user information based on the User.Id passed in.
27 | ModifyUser(user *model.User) error
28 | // ModifyAndUnregister change user information and then unregister the user based on the User.Id passed in.
29 | // It is created to illustrate transaction, no real use.
30 | ModifyAndUnregister(user *model.User) error
31 | }
32 |
33 | // RegistrationUseTxCaseInterface is for use case with transaction. It has registration related functions.
34 | type RegistrationTxUseCaseInterface interface {
35 | // ModifyAndUnregisterWithTx changes user information and then unregisters the user based on the User.Id passed in.
36 | // It supports transaction
37 | // It is created to illustrate transaction, no real life business logic behind it.
38 | ModifyAndUnregisterWithTx(user *model.User) error
39 | }
40 |
41 | // ListUserUseCaseInterface handles different ways to retrieve user information
42 | type ListUserUseCaseInterface interface {
43 | // ListUser retrieves all users as an array of user
44 | ListUser() ([]model.User, error)
45 | // Find retrieves a user based on a user's id
46 | Find(id int) (*model.User, error)
47 | }
48 |
49 | // ListCourseUseCaseInterface handles different ways to retrieve user information
50 | // It is created to show POC of courseDatServiceFactory, no real use
51 | // Only SQL database is implemented for this use case, not couchdb.
52 | type ListCourseUseCaseInterface interface {
53 | // ListCourse retrieves all courses as an array of course
54 | ListCourse() ([]model.Course, error)
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/jfeng45/servicetmpl1
2 |
3 | go 1.12
4 |
5 | require (
6 | github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 // indirect
7 | github.com/flimzy/diff v0.1.7 // indirect
8 | github.com/flimzy/kivik v1.8.1 // indirect
9 | github.com/flimzy/testy v0.1.17 // indirect
10 | github.com/go-kivik/couchdb v1.8.1
11 | github.com/go-kivik/kivik v1.8.1
12 | github.com/go-ozzo/ozzo-validation v3.5.0+incompatible
13 | github.com/go-sql-driver/mysql v1.5.0
14 | github.com/golang/protobuf v1.3.1
15 | github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect
16 | github.com/imdario/mergo v0.3.9 // indirect
17 | github.com/jfeng45/glogger v0.0.0-20200604022340-2e2251ddc0a7
18 | github.com/jfeng45/gtransaction v0.0.0-20200601024905-babe8d366eed
19 | github.com/pkg/errors v0.9.1
20 | github.com/sirupsen/logrus v1.4.2
21 | go.uber.org/multierr v1.5.0 // indirect
22 | go.uber.org/zap v1.13.0
23 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859
24 | google.golang.org/grpc v1.21.1
25 | gopkg.in/yaml.v2 v2.2.2
26 | )
27 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
2 | github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
3 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
4 | github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496 h1:zV3ejI06GQ59hwDQAvmK1qxOQGB3WuVTRoY0okPTAv0=
5 | github.com/asaskevich/govalidator v0.0.0-20200108200545-475eaeb16496/go.mod h1:oGkLhpf+kjZl6xBf758TQhh5XrAeiJv/7FRz/2spLIg=
6 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
7 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
8 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
9 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
10 | github.com/flimzy/diff v0.1.7 h1:DRbd+lN3lY1xVuQrfqvDNsqBwA6RMbClMs6tS5sqWWk=
11 | github.com/flimzy/diff v0.1.7/go.mod h1:lFJtC7SPsK0EroDmGTSrdtWKAxOk3rO+q+e04LL05Hs=
12 | github.com/flimzy/kivik v1.8.1 h1:URl7e0OnfSvAu3ZHQ5BkvzRZlCmyYuDyWUCcPWIHlU0=
13 | github.com/flimzy/kivik v1.8.1/go.mod h1:S2aPycbG0eDFll4wgXt9uacSNkXISPufutnc9sv+mdA=
14 | github.com/flimzy/testy v0.1.17 h1:Y+TUugY6s4B/vrOEPo6SUKafc41W5aiX3qUWvhAPMdI=
15 | github.com/flimzy/testy v0.1.17/go.mod h1:3szguN8NXqgq9bt9Gu8TQVj698PJWmyx/VY1frwwKrM=
16 | github.com/go-kivik/couchdb v1.8.1 h1:2yjmysS48JYpyWTkx2E3c7ASZP8Kh0eABWnkKlV8bbw=
17 | github.com/go-kivik/couchdb v1.8.1/go.mod h1:5XJRkAMpBlEVA4q0ktIZjUPYBjoBmRoiWvwUBzP3BOQ=
18 | github.com/go-kivik/kivik v1.8.1 h1:GScP1mS5wP2km2awszvKzPEjC21lYjQGr3GY+4a/o2U=
19 | github.com/go-kivik/kivik v1.8.1/go.mod h1:nIuJ8z4ikBrVUSk3Ua8NoDqYKULPNjuddjqRvlSUyyQ=
20 | github.com/go-ozzo/ozzo-validation v3.5.0+incompatible h1:sUy/in/P6askYr16XJgTKq/0SZhiWsdg4WZGaLsGQkM=
21 | github.com/go-ozzo/ozzo-validation v3.5.0+incompatible/go.mod h1:gsEKFIVnabGBt6mXmxK0MoFy+cZoTJY6mu5Ll3LVLBU=
22 | github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
23 | github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
24 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
25 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
26 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
27 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
28 | github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
29 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
30 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
31 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
32 | github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
33 | github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
34 | github.com/imdario/mergo v0.3.9 h1:UauaLniWCFHWd+Jp9oCEkTBj8VO/9DKg3PV3VCNMDIg=
35 | github.com/imdario/mergo v0.3.9/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
36 | github.com/jfeng45/glogger v0.0.0-20200411145929-153280a3d7ac h1:nsCqQmVlSmyYwPmlGvQc1sINsfg2S/QY9KOzvkwJ59w=
37 | github.com/jfeng45/glogger v0.0.0-20200411145929-153280a3d7ac/go.mod h1:sf7BgRKEE81JPwRPpmXBmy04V2QEBahAvEi9jrV98vw=
38 | github.com/jfeng45/glogger v0.0.0-20200604022340-2e2251ddc0a7 h1:cSwWfnVNPioi9kn2SmwcMKsSOXzPOwKBHANuaFL+EV4=
39 | github.com/jfeng45/glogger v0.0.0-20200604022340-2e2251ddc0a7/go.mod h1:sf7BgRKEE81JPwRPpmXBmy04V2QEBahAvEi9jrV98vw=
40 | github.com/jfeng45/gtransaction v0.0.0-20200420133858-567c4b2e9a4d h1:ZY8v31Usa06kYBBpHZbyp7kSd/M8MXvmiQdjCR0fZrg=
41 | github.com/jfeng45/gtransaction v0.0.0-20200420133858-567c4b2e9a4d/go.mod h1:mxKpYvSQYKkzao3xy1HyMJC39TqrP/JqVH+nj1PULwY=
42 | github.com/jfeng45/gtransaction v0.0.0-20200531060401-3beeb5e86790 h1:QpPu3lUfYVXAxgY2anR6QpAETgH+L0FopI2l5ncoRZ4=
43 | github.com/jfeng45/gtransaction v0.0.0-20200531060401-3beeb5e86790/go.mod h1:mxKpYvSQYKkzao3xy1HyMJC39TqrP/JqVH+nj1PULwY=
44 | github.com/jfeng45/gtransaction v0.0.0-20200601024905-babe8d366eed h1:gipdFRSJuw90IWWViHvhJuat3vPER+7+fAlHp7o68g8=
45 | github.com/jfeng45/gtransaction v0.0.0-20200601024905-babe8d366eed/go.mod h1:mxKpYvSQYKkzao3xy1HyMJC39TqrP/JqVH+nj1PULwY=
46 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
47 | github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
48 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
49 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
50 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
51 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
52 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
53 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
54 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
55 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
56 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
57 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
58 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
59 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
60 | github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
61 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
62 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
63 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
64 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
65 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
66 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
67 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
68 | go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
69 | go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
70 | go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
71 | go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
72 | go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
73 | go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
74 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
75 | go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
76 | go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM=
77 | go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
78 | go.uber.org/zap v1.13.0 h1:nR6NoDBgAf67s68NhaXbsojM+2gxp3S1hWkHDl27pVU=
79 | go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
80 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
81 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
82 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
83 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
84 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
85 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
86 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
87 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
88 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
89 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
90 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
91 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
92 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
93 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
94 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
95 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc=
96 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
97 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
98 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
99 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
100 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
101 | golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
102 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
103 | golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
104 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
105 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
106 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
107 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
108 | google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
109 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
110 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
111 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
112 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
113 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
114 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
115 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
116 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
117 | honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
118 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
119 |
--------------------------------------------------------------------------------
/script/user.sql:
--------------------------------------------------------------------------------
1 | /*
2 | SQLyog Ultimate
3 | MySQL - 5.0.96-community-log : Database - fasp
4 | *********************************************************************
5 | */
6 |
7 | CREATE DATABASE `service_config` ;
8 |
9 | /*Table structure for table `userinfo` */
10 |
11 | DROP TABLE IF EXISTS `userinfo`;
12 |
13 | CREATE TABLE `userinfo` (
14 | `uid` int(10) NOT NULL auto_increment,
15 | `username` varchar(64) default NULL,
16 | `department` varchar(64) default NULL,
17 | `created` date default NULL,
18 | PRIMARY KEY (`uid`)
19 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
20 |
21 | CREATE TABLE `course` (
22 | `id` INT(10) NOT NULL AUTO_INCREMENT,
23 | `name` VARCHAR(64) DEFAULT NULL,
24 | PRIMARY KEY (`id`)
25 | ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
26 |
27 |
--------------------------------------------------------------------------------
/tool/doc.go:
--------------------------------------------------------------------------------
1 | // Package tool represents a wrapper to third party library. It may enhance or modify some of the functionalities
2 | // of a third party library or create a interface for it.
3 |
4 | package tool
5 |
--------------------------------------------------------------------------------
/tool/timea/timea.go:
--------------------------------------------------------------------------------
1 | package timea
2 |
3 | // The following constants represents different data format layout
4 | const (
5 | FORMAT_ISO8601_DATE = "2006-01-02"
6 | FORMAT_ISO8601_DATE_TIME = "2006-01-02 15:04:05"
7 | FORMAT_ISO8601_DATE_TIME_MILLI = "2006-01-02 15:04:05.000"
8 | FORMAT_ISO8601_DATE_TIME_MILLI_ZONE = "2006-01-02 15:04:05.000Z07:00"
9 | FORMAT_ISO8601_DATE_TIME_MICRO = "2006-01-02 15:04:05.000000"
10 | FORMAT_ISO8601_DATE_TIME_MICRO_ZONE = "2006-01-02 15:04:05.000000Z07:00"
11 | FORMAT_ISO8601_DATE_TIME_NANO = "2006-01-02 15:04:05.000000000"
12 | FORMAT_ISO8601_DATE_TIME_NANO_ZONE = "2006-01-02 15:04:05.00000000007:00"
13 | )
14 |
--------------------------------------------------------------------------------