├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── pkg └── oc │ ├── multi_exporter.go │ ├── setup.go │ ├── trace_endpoints.go │ └── zipkin.go ├── services ├── account │ ├── account.go │ ├── cmd │ │ └── main.go │ ├── cockroachdb │ │ └── repository.go │ ├── implementation │ │ └── service.go │ ├── service.go │ └── transport │ │ ├── endpoints.go │ │ ├── grpc │ │ └── service.go │ │ ├── pb │ │ ├── account.pb.go │ │ └── account.proto │ │ └── request_response.go └── order │ ├── cmd │ └── main.go │ ├── cockroachdb │ └── respository.go │ ├── implementation │ └── service.go │ ├── middleware │ ├── logging.go │ └── service.go │ ├── order.go │ ├── service.go │ └── transport │ ├── endpoints.go │ ├── http │ └── service.go │ └── request_response.go └── setup.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | # IDE 14 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | The updated source code is moved into the repository [go-distsys](https://github.com/shijuvar/go-distsys) 3 | # gokit-examples 4 | Examples for building microservices with Go Kit (gokit.io) 5 | 6 | ## Articles 7 | * [Go Microservices with Go kit: Introduction](https://medium.com/@shijuvar/go-microservices-with-go-kit-introduction-43a757398183) 8 | 9 | ## Technologies Used: 10 | * Go 11 | * Go kit (http://gokit.io) 12 | * CockroachDB 13 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/shijuvar/gokit-examples 2 | 3 | require ( 4 | github.com/basvanbeek/opencensus-gokit-example v0.0.0-20180908172457-a52efcf4f31c 5 | github.com/cockroachdb/apd v1.1.0 // indirect 6 | github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c 7 | github.com/go-kit/kit v0.8.0 8 | github.com/go-logfmt/logfmt v0.3.0 // indirect 9 | github.com/gofrs/uuid v3.1.0+incompatible 10 | github.com/golang/protobuf v1.2.0 11 | github.com/gorilla/mux v1.6.2 12 | github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect 13 | github.com/jackc/pgx v3.2.0+incompatible // indirect 14 | github.com/kr/pretty v0.1.0 // indirect 15 | github.com/lib/pq v1.0.0 16 | github.com/oklog/oklog v0.3.2 17 | github.com/oklog/run v1.0.0 18 | github.com/openzipkin/zipkin-go v0.1.1 19 | github.com/pkg/errors v0.8.0 20 | github.com/satori/go.uuid v1.2.0 // indirect 21 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect 22 | go.opencensus.io v0.18.0 23 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a 24 | google.golang.org/grpc v1.16.0 25 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect 26 | ) 27 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= 3 | github.com/basvanbeek/opencensus-gokit-example v0.0.0-20180908172457-a52efcf4f31c h1:aMN1AvpYEvVTdoTodkMD312E5W6IR/9ro+nFhCfVENQ= 4 | github.com/basvanbeek/opencensus-gokit-example v0.0.0-20180908172457-a52efcf4f31c/go.mod h1:xZyXE73EdSoA5BqQhPUqfaY6sfDILdsgbf6xts/DqYk= 5 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 6 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 7 | github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= 8 | github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= 9 | github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c h1:2zRrJWIt/f9c9HhNHAgrRgq0San5gRRUJTBXLkchal0= 10 | github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= 11 | github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 12 | github.com/go-kit/kit v0.8.0 h1:Wz+5lgoB0kkuqLEc6NVmwRknTKP6dTGbSqvhZtBI/j0= 13 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 14 | github.com/go-logfmt/logfmt v0.3.0 h1:8HUsc87TaSWLKwrnumgC8/YconD2fJQsRJAsWaPg2ic= 15 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 16 | github.com/gofrs/uuid v3.1.0+incompatible h1:q2rtkjaKT4YEr6E1kamy0Ha4RtepWlQBedyHx0uzKwA= 17 | github.com/gofrs/uuid v3.1.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= 18 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 19 | github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= 20 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 21 | github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= 22 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 23 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 24 | github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= 25 | github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= 26 | github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= 27 | github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= 28 | github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY= 29 | github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= 30 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 31 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 32 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 33 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 34 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= 35 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 36 | github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= 37 | github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= 38 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 39 | github.com/oklog/oklog v0.3.2 h1:wVfs8F+in6nTBMkA7CbRw+zZMIB7nNM825cM1wuzoTk= 40 | github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= 41 | github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw= 42 | github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= 43 | github.com/openzipkin/zipkin-go v0.1.1 h1:A/ADD6HaPnAKj3yS7HjGHRK77qi41Hi0DirOOIQAeIw= 44 | github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= 45 | github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= 46 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 47 | github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 48 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 49 | github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= 50 | github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 51 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= 52 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 53 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= 54 | github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= 55 | go.opencensus.io v0.18.0 h1:Mk5rgZcggtbvtAun5aJzAtjKKN/t0R3jJPlWILlv938= 56 | go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= 57 | golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 58 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 59 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 60 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U= 61 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 62 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 63 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 64 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 65 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 66 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 67 | golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 68 | google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= 69 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 70 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= 71 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 72 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw= 73 | google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 74 | google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= 75 | google.golang.org/grpc v1.16.0 h1:dz5IJGuC2BB7qXR5AyHNwAUBhZscK2xVez7mznh72sY= 76 | google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= 77 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 78 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= 79 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 80 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 81 | honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 82 | -------------------------------------------------------------------------------- /pkg/oc/multi_exporter.go: -------------------------------------------------------------------------------- 1 | package oc 2 | 3 | import ( 4 | // stdlib 5 | "errors" 6 | "io" 7 | "strings" 8 | 9 | // external 10 | "go.opencensus.io/stats/view" 11 | "go.opencensus.io/trace" 12 | ) 13 | 14 | type multiExporter struct { 15 | t []trace.Exporter 16 | v []view.Exporter 17 | c []io.Closer 18 | } 19 | 20 | // ExportSpan implements trace.Exporter 21 | func (m *multiExporter) ExportSpan(s *trace.SpanData) { 22 | for _, t := range m.t { 23 | t.ExportSpan(s) 24 | } 25 | } 26 | 27 | // ExportView implements view.Exporter 28 | func (m *multiExporter) ExportView(d *view.Data) { 29 | for _, v := range m.v { 30 | v.ExportView(d) 31 | } 32 | } 33 | 34 | // Close implements io.Closer 35 | func (m *multiExporter) Close() error { 36 | var e []string 37 | 38 | for _, c := range m.c { 39 | if err := c.Close(); err != nil { 40 | e = append(e, err.Error()) 41 | } 42 | } 43 | 44 | if len(e) > 0 { 45 | return errors.New("ERRORS: " + strings.Join(e, " ; ")) 46 | } 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /pkg/oc/setup.go: -------------------------------------------------------------------------------- 1 | package oc 2 | 3 | import ( 4 | // stdlib 5 | "io" 6 | "time" 7 | 8 | // external 9 | "go.opencensus.io/stats/view" 10 | "go.opencensus.io/trace" 11 | ) 12 | 13 | // Setup OpenCensus 14 | func Setup(serviceName string) io.Closer { 15 | // Always trace for this demo. 16 | trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) 17 | 18 | // Report stats at every second. 19 | view.SetReportingPeriod(1 * time.Second) 20 | 21 | zipkinExporter, zipkinCloser := setupZipkin(serviceName) 22 | 23 | exporter := &multiExporter{ 24 | t: []trace.Exporter{ 25 | zipkinExporter, 26 | }, 27 | v: []view.Exporter{}, 28 | c: []io.Closer{ 29 | zipkinCloser, 30 | }, 31 | } 32 | 33 | trace.RegisterExporter(exporter) 34 | view.RegisterExporter(exporter) 35 | 36 | return exporter 37 | } 38 | -------------------------------------------------------------------------------- /pkg/oc/trace_endpoints.go: -------------------------------------------------------------------------------- 1 | package oc 2 | 3 | import ( 4 | "time" 5 | 6 | // external 7 | "github.com/go-kit/kit/endpoint" 8 | kitoc "github.com/go-kit/kit/tracing/opencensus" 9 | "go.opencensus.io/trace" 10 | ) 11 | 12 | // BalancerType used in Retry logic 13 | type BalancerType string 14 | 15 | // BalancerTypes 16 | const ( 17 | Random BalancerType = "random" 18 | RoundRobin BalancerType = "round robin" 19 | ) 20 | 21 | // ClientEndpoint adds our Endpoint Tracing middleware to the existing client 22 | // side endpoint. 23 | func ClientEndpoint(operationName string, attrs ...trace.Attribute) endpoint.Middleware { 24 | attrs = append( 25 | attrs, trace.StringAttribute("gokit.endpoint.type", "client"), 26 | ) 27 | return kitoc.TraceEndpoint( 28 | "gokit/endpoint "+operationName, 29 | kitoc.WithEndpointAttributes(attrs...), 30 | ) 31 | } 32 | 33 | // ServerEndpoint adds our Endpoint Tracing middleware to the existing server 34 | // side endpoint. 35 | func ServerEndpoint(operationName string, attrs ...trace.Attribute) endpoint.Middleware { 36 | attrs = append( 37 | attrs, trace.StringAttribute("gokit.endpoint.type", "server"), 38 | ) 39 | return kitoc.TraceEndpoint( 40 | "gokit/endpoint "+operationName, 41 | kitoc.WithEndpointAttributes(attrs...), 42 | ) 43 | } 44 | 45 | // RetryEndpoint wraps a Go kit lb.Retry endpoint with an annotated span. 46 | func RetryEndpoint( 47 | operationName string, balancer BalancerType, max int, timeout time.Duration, 48 | ) endpoint.Middleware { 49 | return kitoc.TraceEndpoint("gokit/retry "+operationName, 50 | kitoc.WithEndpointAttributes( 51 | trace.StringAttribute("gokit.balancer.type", string(balancer)), 52 | trace.StringAttribute("gokit.retry.timeout", timeout.String()), 53 | trace.Int64Attribute("gokit.retry.max_count", int64(max)), 54 | ), 55 | ) 56 | } 57 | -------------------------------------------------------------------------------- /pkg/oc/zipkin.go: -------------------------------------------------------------------------------- 1 | package oc 2 | 3 | import ( 4 | // stdlib 5 | "io" 6 | 7 | // external 8 | zipkin "github.com/openzipkin/zipkin-go" 9 | reporter "github.com/openzipkin/zipkin-go/reporter/http" 10 | oczipkin "go.opencensus.io/exporter/zipkin" 11 | "go.opencensus.io/trace" 12 | 13 | // project 14 | "github.com/basvanbeek/opencensus-gokit-example/shared/network" 15 | ) 16 | 17 | const ( 18 | zipkinURL = "http://localhost:9411/api/v2/spans" 19 | ) 20 | 21 | func setupZipkin(serviceName string) (trace.Exporter, io.Closer) { 22 | var ( 23 | rep = reporter.NewReporter(zipkinURL) 24 | addr, _ = network.HostIP() 25 | ) 26 | localEndpoint, _ := zipkin.NewEndpoint(serviceName, addr) 27 | 28 | return oczipkin.NewExporter(rep, localEndpoint), rep 29 | } 30 | -------------------------------------------------------------------------------- /services/account/account.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import "context" 4 | 5 | type Customer struct { 6 | ID, Email, Password, Phone string 7 | } 8 | type Repository interface { 9 | CreateCustomer(ctx context.Context, customer Customer) error 10 | } 11 | -------------------------------------------------------------------------------- /services/account/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "fmt" 6 | "net" 7 | "os" 8 | "os/signal" 9 | "syscall" 10 | 11 | "github.com/go-kit/kit/log" 12 | "github.com/go-kit/kit/log/level" 13 | kitoc "github.com/go-kit/kit/tracing/opencensus" 14 | kitgrpc "github.com/go-kit/kit/transport/grpc" 15 | "github.com/oklog/oklog/pkg/group" 16 | "google.golang.org/grpc" 17 | 18 | "github.com/shijuvar/gokit-examples/services/account" 19 | "github.com/shijuvar/gokit-examples/services/account/cockroachdb" 20 | accountsvc "github.com/shijuvar/gokit-examples/services/account/implementation" 21 | "github.com/shijuvar/gokit-examples/services/account/transport" 22 | grpctransport "github.com/shijuvar/gokit-examples/services/account/transport/grpc" 23 | "github.com/shijuvar/gokit-examples/services/account/transport/pb" 24 | ) 25 | 26 | const ( 27 | port = ":50051" 28 | dbsource = "postgresql://shijuvar@localhost:26257/ordersdb?sslmode=disable" 29 | ) 30 | 31 | func main() { 32 | // initialize our structured logger for the service 33 | var logger log.Logger 34 | { 35 | logger = log.NewLogfmtLogger(os.Stderr) 36 | logger = log.NewSyncLogger(logger) 37 | logger = level.NewFilter(logger, level.AllowDebug()) 38 | logger = log.With(logger, 39 | "svc", "account", 40 | "ts", log.DefaultTimestampUTC, 41 | "clr", log.DefaultCaller, 42 | ) 43 | } 44 | 45 | level.Info(logger).Log("msg", "service started") 46 | defer level.Info(logger).Log("msg", "service ended") 47 | 48 | //ctx, cancel := context.WithCancel(context.Background()) 49 | //defer cancel() 50 | 51 | var db *sql.DB 52 | { 53 | var err error 54 | // Connect to the "ordersdb" database 55 | db, err = sql.Open("postgres", dbsource) 56 | if err != nil { 57 | level.Error(logger).Log("exit", err) 58 | os.Exit(-1) 59 | } 60 | } 61 | 62 | // Create Account Service 63 | var svc account.Service 64 | { 65 | repository, err := cockroachdb.New(db, logger) 66 | if err != nil { 67 | level.Error(logger).Log("exit", err) 68 | os.Exit(-1) 69 | } 70 | svc = accountsvc.NewService(repository, logger) 71 | } 72 | 73 | var endpoints transport.Endpoints 74 | { 75 | endpoints = transport.MakeEndpoints(svc) 76 | } 77 | 78 | // set-up grpc transport 79 | var ( 80 | ocTracing = kitoc.GRPCServerTrace() 81 | serverOptions = []kitgrpc.ServerOption{ocTracing} 82 | accountService = grpctransport.NewGRPCServer(endpoints, serverOptions, logger) 83 | grpcListener, _ = net.Listen("tcp", port) 84 | grpcServer = grpc.NewServer() 85 | ) 86 | 87 | var g group.Group 88 | { 89 | /* 90 | Add an actor (function) to the group. 91 | Each actor must be pre-emptable by an interrupt function. 92 | That is, if interrupt is invoked, execute should return. 93 | Also, it must be safe to call interrupt even after execute has returned. 94 | The first actor (function) to return interrupts all running actors. 95 | The error is passed to the interrupt functions, and is returned by Run. 96 | */ 97 | g.Add(func() error { 98 | logger.Log("transport", "gRPC", "addr", port) 99 | pb.RegisterAccountServer(grpcServer, accountService) 100 | return grpcServer.Serve(grpcListener) 101 | }, func(error) { 102 | grpcListener.Close() 103 | }) 104 | } 105 | 106 | { 107 | cancelInterrupt := make(chan struct{}) 108 | g.Add(func() error { 109 | c := make(chan os.Signal, 1) 110 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 111 | select { 112 | case sig := <-c: 113 | return fmt.Errorf("received signal %s", sig) 114 | case <-cancelInterrupt: 115 | return nil 116 | } 117 | }, func(error) { 118 | close(cancelInterrupt) 119 | }) 120 | } 121 | /* 122 | Run all actors (functions) concurrently. When the first actor returns, 123 | all others are interrupted. Run only returns when all actors have exited. 124 | Run returns the error returned by the first exiting actor 125 | */ 126 | level.Error(logger).Log("exit", g.Run()) 127 | } 128 | -------------------------------------------------------------------------------- /services/account/cockroachdb/repository.go: -------------------------------------------------------------------------------- 1 | package cockroachdb 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "errors" 7 | 8 | "github.com/go-kit/kit/log" 9 | 10 | "github.com/shijuvar/gokit-examples/services/account" 11 | ) 12 | 13 | var ( 14 | ErrRepository = errors.New("unable to handle request") 15 | ) 16 | 17 | type repository struct { 18 | db *sql.DB 19 | logger log.Logger 20 | } 21 | 22 | // New returns a concrete repository backed by CockroachDB 23 | func New(db *sql.DB, logger log.Logger) (account.Repository, error) { 24 | // return repository 25 | return &repository{ 26 | db: db, 27 | logger: log.With(logger, "rep", "cockroachdb"), 28 | }, nil 29 | } 30 | 31 | // CreateOrder inserts a new order and its order items into db 32 | func (repo *repository) CreateCustomer(ctx context.Context, customer account.Customer) error { 33 | 34 | // Insert order into the "orders" table. 35 | sql := ` 36 | INSERT INTO customers (id, email, password, phone) 37 | VALUES ($1,$2,$3,$4)` 38 | _, err := repo.db.ExecContext(ctx, sql, customer.ID, customer.Email, customer.Password, customer.Phone) 39 | if err != nil { 40 | return err 41 | } 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /services/account/implementation/service.go: -------------------------------------------------------------------------------- 1 | package implementation 2 | 3 | import ( 4 | "context" 5 | "github.com/go-kit/kit/log" 6 | "github.com/go-kit/kit/log/level" 7 | "github.com/gofrs/uuid" 8 | 9 | "github.com/shijuvar/gokit-examples/services/account" 10 | ) 11 | 12 | // service implements the Order Service 13 | type service struct { 14 | repository account.Repository 15 | logger log.Logger 16 | } 17 | 18 | // NewService creates and returns a new Account service instance 19 | func NewService(rep account.Repository, logger log.Logger) account.Service { 20 | return &service{ 21 | repository: rep, 22 | logger: logger, 23 | } 24 | } 25 | 26 | // Create makes an order 27 | func (s *service) CreateCustomer(ctx context.Context, customer account.Customer) error { 28 | logger := log.With(s.logger, "method", "CreateCustomer") 29 | uuid, _ := uuid.NewV4() 30 | id := uuid.String() 31 | customer.ID = id 32 | 33 | if err := s.repository.CreateCustomer(ctx, customer); err != nil { 34 | level.Error(logger).Log("err", err) 35 | } 36 | return nil 37 | } 38 | -------------------------------------------------------------------------------- /services/account/service.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import "context" 4 | 5 | // Service describes the Account service. 6 | type Service interface { 7 | CreateCustomer(ctx context.Context, customer Customer) error 8 | //AddMoneyToWallet(ctx context.Context, customerID string, amount float64) error 9 | //GetWalletBalance (ctx context.Context, id string) (float64, error) 10 | //MakePayment(ctx context.Context, customerID string, amount float64) error 11 | } 12 | -------------------------------------------------------------------------------- /services/account/transport/endpoints.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/go-kit/kit/endpoint" 7 | 8 | "github.com/shijuvar/gokit-examples/services/account" 9 | ) 10 | 11 | // Endpoints holds all Go kit endpoints for the Order service. 12 | type Endpoints struct { 13 | CreateCustomer endpoint.Endpoint 14 | //AddMoneyToWallet endpoint.Endpoint 15 | //GetWalletBalance endpoint.Endpoint 16 | //MakePayment endpoint.Endpoint 17 | } 18 | 19 | // MakeEndpoints initializes all Go kit endpoints for the Account service. 20 | func MakeEndpoints(s account.Service) Endpoints { 21 | return Endpoints{ 22 | CreateCustomer: makeCreateCustomerEndpoint(s), 23 | //AddMoneyToWallet: makeAddMoneyToWalletEndpoint(s), 24 | //GetWalletBalance: makeGetWalletBalanceEndpoint(s), 25 | //MakePayment: makeMakePaymentEndpoint(s), 26 | } 27 | } 28 | 29 | func makeCreateCustomerEndpoint(s account.Service) endpoint.Endpoint { 30 | return func(ctx context.Context, request interface{}) (interface{}, error) { 31 | req := request.(CreateCustomerRequest) 32 | err := s.CreateCustomer(ctx, req.Customer) 33 | return CreateCustomerResponse{Err: err}, nil 34 | } 35 | } 36 | 37 | //func makeAddMoneyToWalletEndpoint(s account.Service) endpoint.Endpoint { 38 | // return func(ctx context.Context, request interface{}) (interface{}, error) { 39 | // req := request.(AddMoneyToWalletRequest) 40 | // err := s.AddMoneyToWallet(ctx, req.CustomerID, req.Amount) 41 | // return AddMoneyToWalletResponse{Err: err}, nil 42 | // } 43 | //} 44 | // 45 | //func makeGetWalletBalanceEndpoint(s account.Service) endpoint.Endpoint { 46 | // return func(ctx context.Context, request interface{}) (interface{}, error) { 47 | // req := request.(GetWalletBalanceRequest) 48 | // amount, err := s.GetWalletBalance(ctx, req.CustomerID) 49 | // return GetWalletBalanceResponse{Amount: amount, Err: err}, nil 50 | // } 51 | //} 52 | // 53 | //func makeMakePaymentEndpoint(s account.Service) endpoint.Endpoint { 54 | // return func(ctx context.Context, request interface{}) (interface{}, error) { 55 | // req := request.(MakePaymentRequest) 56 | // err := s.MakePayment(ctx, req.CustomerID, req.Amount) 57 | // return MakePaymentResponse{Err: err}, nil 58 | // } 59 | //} 60 | -------------------------------------------------------------------------------- /services/account/transport/grpc/service.go: -------------------------------------------------------------------------------- 1 | package grpc 2 | 3 | import ( 4 | "context" 5 | "github.com/shijuvar/gokit-examples/services/account" 6 | 7 | "github.com/go-kit/kit/log" 8 | kitgrpc "github.com/go-kit/kit/transport/grpc" 9 | oldcontext "golang.org/x/net/context" 10 | "google.golang.org/grpc/codes" 11 | "google.golang.org/grpc/status" 12 | 13 | "github.com/shijuvar/gokit-examples/services/account/transport" 14 | "github.com/shijuvar/gokit-examples/services/account/transport/pb" 15 | ) 16 | 17 | // grpc transport service for Account service. 18 | type grpcServer struct { 19 | createCustomer kitgrpc.Handler 20 | logger log.Logger 21 | } 22 | 23 | // NewGRPCServer returns a new gRPC service for the provided Go kit endpoints 24 | func NewGRPCServer( 25 | endpoints transport.Endpoints, options []kitgrpc.ServerOption, 26 | logger log.Logger, 27 | ) pb.AccountServer { 28 | errorLogger := kitgrpc.ServerErrorLogger(logger) 29 | options = append(options, errorLogger) 30 | 31 | return &grpcServer{ 32 | createCustomer: kitgrpc.NewServer( 33 | endpoints.CreateCustomer, decodeCreateCustomerRequest, encodeCreateCustomerResponse, options..., 34 | ), 35 | logger: logger, 36 | } 37 | } 38 | 39 | // Generate glues the gRPC method to the Go kit service method 40 | func (s *grpcServer) CreateCustomer(ctx oldcontext.Context, req *pb.CreateCustomerRequest) (*pb.CreateCustomerResponse, error) { 41 | _, rep, err := s.createCustomer.ServeGRPC(ctx, req) 42 | if err != nil { 43 | return nil, err 44 | } 45 | return rep.(*pb.CreateCustomerResponse), nil 46 | } 47 | 48 | // decodeCreateCustomerRequest decodes the incoming grpc payload to our go kit payload 49 | func decodeCreateCustomerRequest(_ context.Context, request interface{}) (interface{}, error) { 50 | req := request.(*pb.CreateCustomerRequest) 51 | return transport.CreateCustomerRequest{ 52 | Customer: account.Customer{ 53 | Email: req.Email, 54 | Password: req.Password, 55 | Phone: req.Phone, 56 | }, 57 | }, nil 58 | } 59 | 60 | // encodeCreateCustomerResponse encodes the outgoing go kit payload to the grpc payload 61 | func encodeCreateCustomerResponse(_ context.Context, response interface{}) (interface{}, error) { 62 | res := response.(transport.CreateCustomerResponse) 63 | err := getError(res.Err) 64 | if err == nil { 65 | return &pb.CreateCustomerResponse{}, nil 66 | } 67 | return nil, err 68 | } 69 | 70 | func getError(err error) error { 71 | switch err { 72 | case nil: 73 | return nil 74 | default: 75 | return status.Error(codes.Unknown, err.Error()) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /services/account/transport/pb/account.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-go. DO NOT EDIT. 2 | // source: account.proto 3 | 4 | package pb 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 CreateCustomerRequest struct { 27 | Email string `protobuf:"bytes,1,opt,name=email,proto3" json:"email,omitempty"` 28 | Password string `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"` 29 | Phone string `protobuf:"bytes,3,opt,name=phone,proto3" json:"phone,omitempty"` 30 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 31 | XXX_unrecognized []byte `json:"-"` 32 | XXX_sizecache int32 `json:"-"` 33 | } 34 | 35 | func (m *CreateCustomerRequest) Reset() { *m = CreateCustomerRequest{} } 36 | func (m *CreateCustomerRequest) String() string { return proto.CompactTextString(m) } 37 | func (*CreateCustomerRequest) ProtoMessage() {} 38 | func (*CreateCustomerRequest) Descriptor() ([]byte, []int) { 39 | return fileDescriptor_account_00615f4542184cd9, []int{0} 40 | } 41 | func (m *CreateCustomerRequest) XXX_Unmarshal(b []byte) error { 42 | return xxx_messageInfo_CreateCustomerRequest.Unmarshal(m, b) 43 | } 44 | func (m *CreateCustomerRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 45 | return xxx_messageInfo_CreateCustomerRequest.Marshal(b, m, deterministic) 46 | } 47 | func (dst *CreateCustomerRequest) XXX_Merge(src proto.Message) { 48 | xxx_messageInfo_CreateCustomerRequest.Merge(dst, src) 49 | } 50 | func (m *CreateCustomerRequest) XXX_Size() int { 51 | return xxx_messageInfo_CreateCustomerRequest.Size(m) 52 | } 53 | func (m *CreateCustomerRequest) XXX_DiscardUnknown() { 54 | xxx_messageInfo_CreateCustomerRequest.DiscardUnknown(m) 55 | } 56 | 57 | var xxx_messageInfo_CreateCustomerRequest proto.InternalMessageInfo 58 | 59 | func (m *CreateCustomerRequest) GetEmail() string { 60 | if m != nil { 61 | return m.Email 62 | } 63 | return "" 64 | } 65 | 66 | func (m *CreateCustomerRequest) GetPassword() string { 67 | if m != nil { 68 | return m.Password 69 | } 70 | return "" 71 | } 72 | 73 | func (m *CreateCustomerRequest) GetPhone() string { 74 | if m != nil { 75 | return m.Phone 76 | } 77 | return "" 78 | } 79 | 80 | type CreateCustomerResponse struct { 81 | XXX_NoUnkeyedLiteral struct{} `json:"-"` 82 | XXX_unrecognized []byte `json:"-"` 83 | XXX_sizecache int32 `json:"-"` 84 | } 85 | 86 | func (m *CreateCustomerResponse) Reset() { *m = CreateCustomerResponse{} } 87 | func (m *CreateCustomerResponse) String() string { return proto.CompactTextString(m) } 88 | func (*CreateCustomerResponse) ProtoMessage() {} 89 | func (*CreateCustomerResponse) Descriptor() ([]byte, []int) { 90 | return fileDescriptor_account_00615f4542184cd9, []int{1} 91 | } 92 | func (m *CreateCustomerResponse) XXX_Unmarshal(b []byte) error { 93 | return xxx_messageInfo_CreateCustomerResponse.Unmarshal(m, b) 94 | } 95 | func (m *CreateCustomerResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { 96 | return xxx_messageInfo_CreateCustomerResponse.Marshal(b, m, deterministic) 97 | } 98 | func (dst *CreateCustomerResponse) XXX_Merge(src proto.Message) { 99 | xxx_messageInfo_CreateCustomerResponse.Merge(dst, src) 100 | } 101 | func (m *CreateCustomerResponse) XXX_Size() int { 102 | return xxx_messageInfo_CreateCustomerResponse.Size(m) 103 | } 104 | func (m *CreateCustomerResponse) XXX_DiscardUnknown() { 105 | xxx_messageInfo_CreateCustomerResponse.DiscardUnknown(m) 106 | } 107 | 108 | var xxx_messageInfo_CreateCustomerResponse proto.InternalMessageInfo 109 | 110 | func init() { 111 | proto.RegisterType((*CreateCustomerRequest)(nil), "pb.CreateCustomerRequest") 112 | proto.RegisterType((*CreateCustomerResponse)(nil), "pb.CreateCustomerResponse") 113 | } 114 | 115 | // Reference imports to suppress errors if they are not otherwise used. 116 | var _ context.Context 117 | var _ grpc.ClientConn 118 | 119 | // This is a compile-time assertion to ensure that this generated file 120 | // is compatible with the grpc package it is being compiled against. 121 | const _ = grpc.SupportPackageIsVersion4 122 | 123 | // AccountClient is the client API for Account service. 124 | // 125 | // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. 126 | type AccountClient interface { 127 | CreateCustomer(ctx context.Context, in *CreateCustomerRequest, opts ...grpc.CallOption) (*CreateCustomerResponse, error) 128 | } 129 | 130 | type accountClient struct { 131 | cc *grpc.ClientConn 132 | } 133 | 134 | func NewAccountClient(cc *grpc.ClientConn) AccountClient { 135 | return &accountClient{cc} 136 | } 137 | 138 | func (c *accountClient) CreateCustomer(ctx context.Context, in *CreateCustomerRequest, opts ...grpc.CallOption) (*CreateCustomerResponse, error) { 139 | out := new(CreateCustomerResponse) 140 | err := c.cc.Invoke(ctx, "/pb.Account/CreateCustomer", in, out, opts...) 141 | if err != nil { 142 | return nil, err 143 | } 144 | return out, nil 145 | } 146 | 147 | // AccountServer is the server API for Account service. 148 | type AccountServer interface { 149 | CreateCustomer(context.Context, *CreateCustomerRequest) (*CreateCustomerResponse, error) 150 | } 151 | 152 | func RegisterAccountServer(s *grpc.Server, srv AccountServer) { 153 | s.RegisterService(&_Account_serviceDesc, srv) 154 | } 155 | 156 | func _Account_CreateCustomer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { 157 | in := new(CreateCustomerRequest) 158 | if err := dec(in); err != nil { 159 | return nil, err 160 | } 161 | if interceptor == nil { 162 | return srv.(AccountServer).CreateCustomer(ctx, in) 163 | } 164 | info := &grpc.UnaryServerInfo{ 165 | Server: srv, 166 | FullMethod: "/pb.Account/CreateCustomer", 167 | } 168 | handler := func(ctx context.Context, req interface{}) (interface{}, error) { 169 | return srv.(AccountServer).CreateCustomer(ctx, req.(*CreateCustomerRequest)) 170 | } 171 | return interceptor(ctx, in, info, handler) 172 | } 173 | 174 | var _Account_serviceDesc = grpc.ServiceDesc{ 175 | ServiceName: "pb.Account", 176 | HandlerType: (*AccountServer)(nil), 177 | Methods: []grpc.MethodDesc{ 178 | { 179 | MethodName: "CreateCustomer", 180 | Handler: _Account_CreateCustomer_Handler, 181 | }, 182 | }, 183 | Streams: []grpc.StreamDesc{}, 184 | Metadata: "account.proto", 185 | } 186 | 187 | func init() { proto.RegisterFile("account.proto", fileDescriptor_account_00615f4542184cd9) } 188 | 189 | var fileDescriptor_account_00615f4542184cd9 = []byte{ 190 | // 164 bytes of a gzipped FileDescriptorProto 191 | 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4d, 0x4c, 0x4e, 0xce, 192 | 0x2f, 0xcd, 0x2b, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2a, 0x48, 0x52, 0x8a, 0xe7, 193 | 0x12, 0x75, 0x2e, 0x4a, 0x4d, 0x2c, 0x49, 0x75, 0x2e, 0x2d, 0x2e, 0xc9, 0xcf, 0x4d, 0x2d, 0x0a, 194 | 0x4a, 0x2d, 0x2c, 0x4d, 0x2d, 0x2e, 0x11, 0x12, 0xe1, 0x62, 0x4d, 0xcd, 0x4d, 0xcc, 0xcc, 0x91, 195 | 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x82, 0x70, 0x84, 0xa4, 0xb8, 0x38, 0x0a, 0x12, 0x8b, 0x8b, 196 | 0xcb, 0xf3, 0x8b, 0x52, 0x24, 0x98, 0xc0, 0x12, 0x70, 0x3e, 0x48, 0x47, 0x41, 0x46, 0x7e, 0x5e, 197 | 0xaa, 0x04, 0x33, 0x44, 0x07, 0x98, 0xa3, 0x24, 0xc1, 0x25, 0x86, 0x6e, 0x41, 0x71, 0x41, 0x7e, 198 | 0x5e, 0x71, 0xaa, 0x51, 0x08, 0x17, 0xbb, 0x23, 0xc4, 0x3d, 0x42, 0x9e, 0x5c, 0x7c, 0xa8, 0x8a, 199 | 0x84, 0x24, 0xf5, 0x0a, 0x92, 0xf4, 0xb0, 0xba, 0x4c, 0x4a, 0x0a, 0x9b, 0x14, 0xc4, 0x4c, 0x25, 200 | 0x86, 0x24, 0x36, 0xb0, 0xdf, 0x8c, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x2b, 0xe3, 0x17, 0xd9, 201 | 0xec, 0x00, 0x00, 0x00, 202 | } 203 | -------------------------------------------------------------------------------- /services/account/transport/pb/account.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package pb; 4 | 5 | service Account { 6 | rpc CreateCustomer (CreateCustomerRequest) returns (CreateCustomerResponse) {} 7 | } 8 | 9 | message CreateCustomerRequest { 10 | string email =1; 11 | string password =2; 12 | string phone =3; 13 | } 14 | 15 | message CreateCustomerResponse { 16 | } 17 | -------------------------------------------------------------------------------- /services/account/transport/request_response.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import "github.com/shijuvar/gokit-examples/services/account" 4 | 5 | type ( 6 | CreateCustomerRequest struct { 7 | Customer account.Customer 8 | } 9 | CreateCustomerResponse struct { 10 | Err error 11 | } 12 | AddMoneyToWalletRequest struct { 13 | CustomerID string 14 | Amount float64 15 | } 16 | AddMoneyToWalletResponse struct { 17 | Err error 18 | } 19 | GetWalletBalanceRequest struct { 20 | CustomerID string 21 | } 22 | GetWalletBalanceResponse struct { 23 | Amount float64 24 | Err error 25 | } 26 | MakePaymentRequest struct { 27 | CustomerID string 28 | Amount float64 29 | } 30 | MakePaymentResponse struct { 31 | Err error 32 | } 33 | ) 34 | -------------------------------------------------------------------------------- /services/order/cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "database/sql" 5 | "flag" 6 | "fmt" 7 | "github.com/shijuvar/gokit-examples/services/order/middleware" 8 | "net/http" 9 | "os" 10 | "os/signal" 11 | "syscall" 12 | 13 | "github.com/go-kit/kit/log" 14 | "github.com/go-kit/kit/log/level" 15 | kitoc "github.com/go-kit/kit/tracing/opencensus" 16 | kithttp "github.com/go-kit/kit/transport/http" 17 | _ "github.com/lib/pq" 18 | "github.com/shijuvar/gokit-examples/pkg/oc" 19 | 20 | "github.com/shijuvar/gokit-examples/services/order" 21 | "github.com/shijuvar/gokit-examples/services/order/cockroachdb" 22 | ordersvc "github.com/shijuvar/gokit-examples/services/order/implementation" 23 | "github.com/shijuvar/gokit-examples/services/order/transport" 24 | httptransport "github.com/shijuvar/gokit-examples/services/order/transport/http" 25 | ) 26 | 27 | func main() { 28 | var ( 29 | httpAddr = flag.String("http.addr", ":8080", "HTTP listen address") 30 | ) 31 | flag.Parse() 32 | // initialize our OpenCensus configuration and defer a clean-up 33 | defer oc.Setup("order").Close() 34 | var logger log.Logger 35 | { 36 | logger = log.NewLogfmtLogger(os.Stderr) 37 | logger = log.NewSyncLogger(logger) 38 | logger = level.NewFilter(logger, level.AllowDebug()) 39 | logger = log.With(logger, 40 | "svc", "order", 41 | "ts", log.DefaultTimestampUTC, 42 | "caller", log.DefaultCaller, 43 | ) 44 | } 45 | 46 | level.Info(logger).Log("msg", "service started") 47 | defer level.Info(logger).Log("msg", "service ended") 48 | 49 | var db *sql.DB 50 | { 51 | var err error 52 | // Connect to the "ordersdb" database 53 | db, err = sql.Open("postgres", 54 | "postgresql://shijuvar@localhost:26257/ordersdb?sslmode=disable") 55 | if err != nil { 56 | level.Error(logger).Log("exit", err) 57 | os.Exit(-1) 58 | } 59 | } 60 | 61 | // Create Order Service 62 | var svc order.Service 63 | { 64 | repository, err := cockroachdb.New(db, logger) 65 | if err != nil { 66 | level.Error(logger).Log("exit", err) 67 | os.Exit(-1) 68 | } 69 | svc = ordersvc.NewService(repository, logger) 70 | // Add service middleware here 71 | // Logging middleware 72 | svc = middleware.LoggingMiddleware(logger)(svc) 73 | } 74 | // Create Go kit endpoints for the Order Service 75 | // Then decorates with endpoint middlewares 76 | var endpoints transport.Endpoints 77 | { 78 | endpoints = transport.MakeEndpoints(svc) 79 | // Add endpoint level middlewares here 80 | // Trace server side endpoints with open census 81 | endpoints = transport.Endpoints{ 82 | Create: oc.ServerEndpoint("Create")(endpoints.Create), 83 | GetByID: oc.ServerEndpoint("GetByID")(endpoints.GetByID), 84 | ChangeStatus: oc.ServerEndpoint("ChangeStatus")(endpoints.ChangeStatus), 85 | } 86 | 87 | } 88 | var h http.Handler 89 | { 90 | ocTracing := kitoc.HTTPServerTrace() 91 | serverOptions := []kithttp.ServerOption{ocTracing} 92 | h = httptransport.NewService(endpoints, serverOptions, logger) 93 | } 94 | 95 | errs := make(chan error) 96 | go func() { 97 | c := make(chan os.Signal, 1) 98 | signal.Notify(c, syscall.SIGINT, syscall.SIGTERM) 99 | errs <- fmt.Errorf("%s", <-c) 100 | }() 101 | 102 | go func() { 103 | level.Info(logger).Log("transport", "HTTP", "addr", *httpAddr) 104 | server := &http.Server{ 105 | Addr: *httpAddr, 106 | Handler: h, 107 | } 108 | errs <- server.ListenAndServe() 109 | }() 110 | 111 | level.Error(logger).Log("exit", <-errs) 112 | } 113 | -------------------------------------------------------------------------------- /services/order/cockroachdb/respository.go: -------------------------------------------------------------------------------- 1 | package cockroachdb 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "errors" 7 | "github.com/go-kit/kit/log/level" 8 | 9 | "github.com/cockroachdb/cockroach-go/crdb" 10 | "github.com/go-kit/kit/log" 11 | 12 | "github.com/shijuvar/gokit-examples/services/order" 13 | ) 14 | 15 | var ( 16 | ErrRepository = errors.New("unable to handle request") 17 | ) 18 | 19 | type repository struct { 20 | db *sql.DB 21 | logger log.Logger 22 | } 23 | 24 | // New returns a concrete repository backed by CockroachDB 25 | func New(db *sql.DB, logger log.Logger) (order.Repository, error) { 26 | // return repository 27 | return &repository{ 28 | db: db, 29 | logger: log.With(logger, "rep", "cockroachdb"), 30 | }, nil 31 | } 32 | 33 | // CreateOrder inserts a new order and its order items into db 34 | func (repo *repository) CreateOrder(ctx context.Context, order order.Order) error { 35 | 36 | // Run a transaction to sync the query model. 37 | err := crdb.ExecuteTx(ctx, repo.db, nil, func(tx *sql.Tx) error { 38 | return createOrder(tx, order) 39 | }) 40 | if err != nil { 41 | return err 42 | } 43 | return nil 44 | } 45 | 46 | func createOrder(tx *sql.Tx, order order.Order) error { 47 | 48 | // Insert order into the "orders" table. 49 | sql := ` 50 | INSERT INTO orders (id, customerid, status, createdon, restaurantid) 51 | VALUES ($1,$2,$3,$4,$5)` 52 | _, err := tx.Exec(sql, order.ID, order.CustomerID, order.Status, order.CreatedOn, order.RestaurantId) 53 | if err != nil { 54 | return err 55 | } 56 | // Insert order items into the "orderitems" table. 57 | // Because it's store for read model, we can insert denormalized data 58 | for _, v := range order.OrderItems { 59 | sql = ` 60 | INSERT INTO orderitems (orderid, customerid, code, name, unitprice, quantity) 61 | VALUES ($1,$2,$3,$4,$5,$6)` 62 | 63 | _, err := tx.Exec(sql, order.ID, order.CustomerID, v.ProductCode, v.Name, v.UnitPrice, v.Quantity) 64 | if err != nil { 65 | return err 66 | } 67 | } 68 | return nil 69 | } 70 | 71 | // ChangeOrderStatus changes the order status 72 | func (repo *repository) ChangeOrderStatus(ctx context.Context, orderId string, status string) error { 73 | sql := ` 74 | UPDATE orders 75 | SET status=$2 76 | WHERE id=$1` 77 | 78 | _, err := repo.db.ExecContext(ctx, sql, orderId, status) 79 | if err != nil { 80 | return err 81 | } 82 | return nil 83 | } 84 | 85 | // GetOrderByID query the order by given id 86 | func (repo *repository) GetOrderByID(ctx context.Context, id string) (order.Order, error) { 87 | var orderRow = order.Order{} 88 | if err := repo.db.QueryRowContext(ctx, 89 | "SELECT id, customerid, status, createdon, restaurantid FROM orders WHERE id = $1", 90 | id). 91 | Scan( 92 | &orderRow.ID, &orderRow.CustomerID, &orderRow.Status, &orderRow.CreatedOn, &orderRow.RestaurantId, 93 | ); err != nil { 94 | level.Error(repo.logger).Log("err", err.Error()) 95 | return orderRow, err 96 | } 97 | // ToDo: Query order items from orderitems table 98 | return orderRow, nil 99 | } 100 | 101 | // Close implements DB.Close 102 | func (repo *repository) Close() error { 103 | return repo.db.Close() 104 | } 105 | -------------------------------------------------------------------------------- /services/order/implementation/service.go: -------------------------------------------------------------------------------- 1 | package implementation 2 | 3 | import ( 4 | "context" 5 | "database/sql" 6 | "time" 7 | 8 | "github.com/go-kit/kit/log" 9 | "github.com/go-kit/kit/log/level" 10 | "github.com/gofrs/uuid" 11 | 12 | ordersvc "github.com/shijuvar/gokit-examples/services/order" 13 | ) 14 | 15 | // service implements the Order Service 16 | type service struct { 17 | repository ordersvc.Repository 18 | logger log.Logger 19 | } 20 | 21 | // NewService creates and returns a new Order service instance 22 | func NewService(rep ordersvc.Repository, logger log.Logger) ordersvc.Service { 23 | return &service{ 24 | repository: rep, 25 | logger: logger, 26 | } 27 | } 28 | 29 | // Create makes an order 30 | func (s *service) Create(ctx context.Context, order ordersvc.Order) (string, error) { 31 | logger := log.With(s.logger, "method", "Create") 32 | uuid, _ := uuid.NewV4() 33 | id := uuid.String() 34 | order.ID = id 35 | order.Status = "Pending" 36 | order.CreatedOn = time.Now().Unix() 37 | 38 | if err := s.repository.CreateOrder(ctx, order); err != nil { 39 | level.Error(logger).Log("err", err) 40 | return "", ordersvc.ErrCmdRepository 41 | } 42 | return id, nil 43 | } 44 | 45 | // GetByID returns an order given by id 46 | func (s *service) GetByID(ctx context.Context, id string) (ordersvc.Order, error) { 47 | logger := log.With(s.logger, "method", "GetByID") 48 | order, err := s.repository.GetOrderByID(ctx, id) 49 | if err != nil { 50 | level.Error(logger).Log("err", err) 51 | if err == sql.ErrNoRows { 52 | return order, ordersvc.ErrOrderNotFound 53 | } 54 | return order, ordersvc.ErrQueryRepository 55 | } 56 | return order, nil 57 | } 58 | 59 | // ChangeStatus changes the status of an order 60 | func (s *service) ChangeStatus(ctx context.Context, id string, status string) error { 61 | logger := log.With(s.logger, "method", "ChangeStatus") 62 | if err := s.repository.ChangeOrderStatus(ctx, id, status); err != nil { 63 | level.Error(logger).Log("err", err) 64 | return ordersvc.ErrCmdRepository 65 | } 66 | return nil 67 | } 68 | -------------------------------------------------------------------------------- /services/order/middleware/logging.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/go-kit/kit/log" 8 | 9 | "github.com/shijuvar/gokit-examples/services/order" 10 | ) 11 | 12 | func LoggingMiddleware(logger log.Logger) Middleware { 13 | return func(next order.Service) order.Service { 14 | return &loggingMiddleware{ 15 | next: next, 16 | logger: logger, 17 | } 18 | } 19 | } 20 | 21 | type loggingMiddleware struct { 22 | next order.Service 23 | logger log.Logger 24 | } 25 | 26 | func (mw loggingMiddleware) Create(ctx context.Context, order order.Order) (id string, err error) { 27 | defer func(begin time.Time) { 28 | mw.logger.Log("method", "Create", "CustomerID", order.CustomerID, "took", time.Since(begin), "err", err) 29 | }(time.Now()) 30 | return mw.next.Create(ctx, order) 31 | } 32 | 33 | func (mw loggingMiddleware) GetByID(ctx context.Context, id string) (order order.Order, err error) { 34 | defer func(begin time.Time) { 35 | mw.logger.Log("method", "GetByID", "OrderID", id, "took", time.Since(begin), "err", err) 36 | }(time.Now()) 37 | return mw.next.GetByID(ctx, id) 38 | } 39 | 40 | func (mw loggingMiddleware) ChangeStatus(ctx context.Context, id string, status string) (err error) { 41 | defer func(begin time.Time) { 42 | mw.logger.Log("method", "ChangeStatus", "OrderID", id, "took", time.Since(begin), "err", err) 43 | }(time.Now()) 44 | return mw.next.ChangeStatus(ctx, id, status) 45 | } 46 | -------------------------------------------------------------------------------- /services/order/middleware/service.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import "github.com/shijuvar/gokit-examples/services/order" 4 | 5 | // Middleware describes a service middleware. 6 | type Middleware func(service order.Service) order.Service 7 | -------------------------------------------------------------------------------- /services/order/order.go: -------------------------------------------------------------------------------- 1 | package order 2 | 3 | import "context" 4 | 5 | // Order represents an order 6 | type Order struct { 7 | ID string `json:"id,omitempty"` 8 | CustomerID string `json:"customer_id"` 9 | Status string `json:"status"` 10 | CreatedOn int64 `json:"created_on,omitempty"` 11 | RestaurantId string `json:"restaurant_id"` 12 | OrderItems []OrderItem `json:"order_items,omitempty"` 13 | } 14 | 15 | // OrderItem represents items in an order 16 | type OrderItem struct { 17 | ProductCode string `json:"product_code"` 18 | Name string `json:"name"` 19 | UnitPrice float32 `json:"unit_price"` 20 | Quantity int32 `json:"quantity"` 21 | } 22 | 23 | // Repository describes the persistence on order model 24 | type Repository interface { 25 | CreateOrder(ctx context.Context, order Order) error 26 | GetOrderByID(ctx context.Context, id string) (Order, error) 27 | ChangeOrderStatus(ctx context.Context, id string, status string) error 28 | } 29 | -------------------------------------------------------------------------------- /services/order/service.go: -------------------------------------------------------------------------------- 1 | package order 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | ) 7 | 8 | var ( 9 | ErrOrderNotFound = errors.New("order not found") 10 | ErrCmdRepository = errors.New("unable to command repository") 11 | ErrQueryRepository = errors.New("unable to query repository") 12 | ) 13 | 14 | // Service describes the Order service. 15 | type Service interface { 16 | Create(ctx context.Context, order Order) (string, error) 17 | GetByID(ctx context.Context, id string) (Order, error) 18 | ChangeStatus(ctx context.Context, id string, status string) error 19 | } 20 | -------------------------------------------------------------------------------- /services/order/transport/endpoints.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/go-kit/kit/endpoint" 7 | 8 | "github.com/shijuvar/gokit-examples/services/order" 9 | ) 10 | 11 | // Endpoints holds all Go kit endpoints for the Order service. 12 | type Endpoints struct { 13 | Create endpoint.Endpoint 14 | GetByID endpoint.Endpoint 15 | ChangeStatus endpoint.Endpoint 16 | } 17 | 18 | // MakeEndpoints initializes all Go kit endpoints for the Order service. 19 | func MakeEndpoints(s order.Service) Endpoints { 20 | return Endpoints{ 21 | Create: makeCreateEndpoint(s), 22 | GetByID: makeGetByIDEndpoint(s), 23 | ChangeStatus: makeChangeStatusEndpoint(s), 24 | } 25 | } 26 | 27 | func makeCreateEndpoint(s order.Service) endpoint.Endpoint { 28 | return func(ctx context.Context, request interface{}) (interface{}, error) { 29 | req := request.(CreateRequest) // type assertion 30 | id, err := s.Create(ctx, req.Order) 31 | return CreateResponse{ID: id, Err: err}, nil 32 | } 33 | } 34 | 35 | func makeGetByIDEndpoint(s order.Service) endpoint.Endpoint { 36 | return func(ctx context.Context, request interface{}) (interface{}, error) { 37 | req := request.(GetByIDRequest) 38 | orderRes, err := s.GetByID(ctx, req.ID) 39 | return GetByIDResponse{Order: orderRes, Err: err}, nil 40 | } 41 | } 42 | 43 | func makeChangeStatusEndpoint(s order.Service) endpoint.Endpoint { 44 | return func(ctx context.Context, request interface{}) (interface{}, error) { 45 | req := request.(ChangeStatusRequest) 46 | err := s.ChangeStatus(ctx, req.ID, req.Status) 47 | return ChangeStatusResponse{Err: err}, nil 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /services/order/transport/http/service.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "github.com/shijuvar/gokit-examples/services/order" 8 | "net/http" 9 | 10 | "github.com/go-kit/kit/log" 11 | kithttp "github.com/go-kit/kit/transport/http" 12 | "github.com/gorilla/mux" 13 | 14 | "github.com/shijuvar/gokit-examples/services/order/transport" 15 | ) 16 | 17 | var ( 18 | ErrBadRouting = errors.New("bad routing") 19 | ) 20 | 21 | // NewService wires Go kit endpoints to the HTTP transport. 22 | func NewService( 23 | svcEndpoints transport.Endpoints, options []kithttp.ServerOption, logger log.Logger, 24 | ) http.Handler { 25 | // set-up router and initialize http endpoints 26 | var ( 27 | r = mux.NewRouter() 28 | errorLogger = kithttp.ServerErrorLogger(logger) 29 | errorEncoder = kithttp.ServerErrorEncoder(encodeErrorResponse) 30 | ) 31 | options = append(options, errorLogger, errorEncoder) 32 | //options := []kithttp.ServerOption{ 33 | // kithttp.ServerErrorLogger(logger), 34 | // kithttp.ServerErrorEncoder(encodeError), 35 | //} 36 | // HTTP Post - /orders 37 | r.Methods("POST").Path("/orders").Handler(kithttp.NewServer( 38 | svcEndpoints.Create, 39 | decodeCreateRequest, 40 | encodeResponse, 41 | options..., 42 | )) 43 | 44 | // HTTP Post - /orders/{id} 45 | r.Methods("GET").Path("/orders/{id}").Handler(kithttp.NewServer( 46 | svcEndpoints.GetByID, 47 | decodeGetByIDRequest, 48 | encodeResponse, 49 | options..., 50 | )) 51 | 52 | // HTTP Post - /orders/status 53 | r.Methods("POST").Path("/orders/status").Handler(kithttp.NewServer( 54 | svcEndpoints.ChangeStatus, 55 | decodeChangeStausRequest, 56 | encodeResponse, 57 | options..., 58 | )) 59 | return r 60 | } 61 | 62 | func decodeCreateRequest(_ context.Context, r *http.Request) (request interface{}, err error) { 63 | var req transport.CreateRequest 64 | if e := json.NewDecoder(r.Body).Decode(&req.Order); e != nil { 65 | return nil, e 66 | } 67 | return req, nil 68 | } 69 | 70 | func decodeGetByIDRequest(_ context.Context, r *http.Request) (request interface{}, err error) { 71 | vars := mux.Vars(r) 72 | id, ok := vars["id"] 73 | if !ok { 74 | return nil, ErrBadRouting 75 | } 76 | return transport.GetByIDRequest{ID: id}, nil 77 | } 78 | 79 | func decodeChangeStausRequest(_ context.Context, r *http.Request) (request interface{}, err error) { 80 | var req transport.ChangeStatusRequest 81 | if e := json.NewDecoder(r.Body).Decode(&req); e != nil { 82 | return nil, e 83 | } 84 | return req, nil 85 | } 86 | 87 | func encodeResponse(ctx context.Context, w http.ResponseWriter, response interface{}) error { 88 | if e, ok := response.(errorer); ok && e.error() != nil { 89 | // Not a Go kit transport error, but a business-logic error. 90 | // Provide those as HTTP errors. 91 | encodeErrorResponse(ctx, e.error(), w) 92 | return nil 93 | } 94 | w.Header().Set("Content-Type", "application/json; charset=utf-8") 95 | return json.NewEncoder(w).Encode(response) 96 | } 97 | 98 | type errorer interface { 99 | error() error 100 | } 101 | 102 | func encodeErrorResponse(_ context.Context, err error, w http.ResponseWriter) { 103 | if err == nil { 104 | panic("encodeError with nil error") 105 | } 106 | w.Header().Set("Content-Type", "application/json; charset=utf-8") 107 | w.WriteHeader(codeFrom(err)) 108 | json.NewEncoder(w).Encode(map[string]interface{}{ 109 | "error": err.Error(), 110 | }) 111 | } 112 | 113 | func codeFrom(err error) int { 114 | switch err { 115 | case order.ErrOrderNotFound: 116 | return http.StatusBadRequest 117 | default: 118 | return http.StatusInternalServerError 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /services/order/transport/request_response.go: -------------------------------------------------------------------------------- 1 | package transport 2 | 3 | import ( 4 | "github.com/shijuvar/gokit-examples/services/order" 5 | ) 6 | 7 | // CreateRequest holds the request parameters for the Create method. 8 | type CreateRequest struct { 9 | Order order.Order 10 | } 11 | 12 | // CreateResponse holds the response values for the Create method. 13 | type CreateResponse struct { 14 | ID string `json:"id"` 15 | Err error `json:"error,omitempty"` 16 | } 17 | 18 | // GetByIDRequest holds the request parameters for the GetByID method. 19 | type GetByIDRequest struct { 20 | ID string 21 | } 22 | 23 | // GetByIDResponse holds the response values for the GetByID method. 24 | type GetByIDResponse struct { 25 | Order order.Order `json:"order"` 26 | Err error `json:"error,omitempty"` 27 | } 28 | 29 | // ChangeStatusRequest holds the request parameters for the ChangeStatus method. 30 | type ChangeStatusRequest struct { 31 | ID string `json:"id"` 32 | Status string `json:"status"` 33 | } 34 | 35 | // ChangeStatusResponse holds the response values for the ChangeStatus method. 36 | type ChangeStatusResponse struct { 37 | Err error `json:"error,omitempty"` 38 | } 39 | -------------------------------------------------------------------------------- /setup.md: -------------------------------------------------------------------------------- 1 | ### Start CockroachDB cluster 2 | 3 | cockroach start --insecure \ 4 | --store=ordersdb-1 \ 5 | --host=localhost 6 | --background 7 | 8 | cockroach start \ 9 | --insecure \ 10 | --store=ordersdb-2 \ 11 | --host=localhost \ 12 | --port=26258 \ 13 | --http-port=8081 \ 14 | --join=localhost:26257 15 | --background 16 | 17 | cockroach start --insecure \ 18 | --store=ordersdb-3 \ 19 | --host=localhost \ 20 | --port=26259 \ 21 | --http-port=8082 \ 22 | --join=localhost:26257 \ 23 | --background 24 | 25 | ### Start Zipkin in Docker 26 | docker run -d -p 9411:9411 openzipkin/zipkin 27 | --------------------------------------------------------------------------------