{"id":13823376,"url":"https://github.com/1995parham/koochooloo","last_synced_at":"2025-04-09T22:52:22.429Z","repository":{"id":37987019,"uuid":"233512608","full_name":"1995parham/koochooloo","owner":"1995parham","description":"Make your URLs shorter (smaller) and more memorable in Go","archived":false,"fork":false,"pushed_at":"2025-04-01T12:45:50.000Z","size":1059,"stargazers_count":88,"open_issues_count":1,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-09T22:52:18.144Z","etag":null,"topics":["golang","golang-application","golang-examples","learn-go-by-simple-demo","learn-to-code","learning-by-doing","url-shortener"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/1995parham.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-01-13T04:42:49.000Z","updated_at":"2025-04-01T12:44:50.000Z","dependencies_parsed_at":"2024-01-13T15:45:18.160Z","dependency_job_id":"9b9529e3-3818-40a8-b0fb-a5b89092875a","html_url":"https://github.com/1995parham/koochooloo","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1995parham%2Fkoochooloo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1995parham%2Fkoochooloo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1995parham%2Fkoochooloo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/1995parham%2Fkoochooloo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/1995parham","download_url":"https://codeload.github.com/1995parham/koochooloo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248125641,"owners_count":21051766,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["golang","golang-application","golang-examples","learn-go-by-simple-demo","learn-to-code","learning-by-doing","url-shortener"],"created_at":"2024-08-04T09:00:32.329Z","updated_at":"2025-04-09T22:52:22.393Z","avatar_url":"https://github.com/1995parham.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e🐼 koochooloo 🌰\u003c/h1\u003e\n\u003ch6 align=\"center\"\u003eI want to dedicate this project to my love ❤️\u003c/h6\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"./assets/koochooloo.png\" height=\"250px\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg alt=\"GitHub Workflow Status\" src=\"https://img.shields.io/github/actions/workflow/status/1995parham/koochooloo/test.yaml?logo=github\u0026style=for-the-badge\"\u003e\n\u003cimg alt=\"Codecov\" src=\"https://img.shields.io/codecov/c/github/1995parham/koochooloo?logo=codecov\u0026style=for-the-badge\"\u003e\n\u003cimg alt=\"GitHub repo size\" src=\"https://img.shields.io/github/repo-size/1995parham/koochooloo?logo=github\u0026style=for-the-badge\"\u003e\n \u003c/p\u003e\n\n\u003e a Persian word which means \"small\" or \"li'l\". It is often used to refer to a girl when flirting, (with the meaning, li'l girl)\n\u003e\n\u003e Urban Dictionary\n\n## Introduction\n\nWelcome to **Koochooloo**: an elegant, practical project crafted to streamline\nthe development of Golang applications. Boasting a well-organized architecture, \n**Koochooloo** integrates vital features such as database handling and configuration \nmanagement, exemplifying the best practices in building robust ReST applications with Go.\n\n### Features\n\n- **Strong Typing**: Unyielding commitment to strong typing, enhancing readability and maintainability.\n- **No Globals or `init` Functions**: Eschews globals and the complexities of `init` functions to keep things simple.\n- **Standardized Naming**: Adherence to the de-facto standard of singular package names, inspired by 'project-layout' principles.\n- **Independent Packages**: Each package is crafted to function independently, making the addition of new features seamless.\n- **Intuitive Structure**: Navigate with ease through a codebase that's designed for clarity.\n\n### Technology\n\nLeveraging `fx` as our dependency injection framework, **Koochooloo** delivers:\n\n- **No Code Generation**: Utilize powerful dependency injection without any code bloat from code generation.\n- **Test-Friendly**: An environment that supports and simplifies the use of `fx` in testing with the same ease as in production.\n\n### Your Development Companion\n\nEmbark on a journey with **koochooloo** and redefine your approach to creating ReSTful applications in Go.\nWhether you're expanding your skillset or building a solid foundation for complex applications, \n**Koochooloo** is your partner in efficient, clean, and scalable software design.\n\n## Structure\n\n### Binaries\n\nFirst of all, `cmd` package contains the binaries of this project with use of [cobra](https://github.com/spf13/cobra).\nIt is good to have a simple binaries for tasks like database migrations that can be run on initiation phase of project.\nEach binary has its `main.go` in its package and registers itself with a `Register` function.\nIn the `root.go` of `cmd` these `Register` functions from sub-commands are called.\nHere is an example for register function:\n\n```go\n// Register server command.\nfunc Register(root *cobra.Command) {\n root.AddCommand(\n  \u0026cobra.Command{\n   Use:   \"server\",\n   Short: \"Run server to serve the requests\",\n   Run: func(_ *cobra.Command, _ []string) {\n    fx.New(\n        fx.Provide(config.Provide),\n        fx.Invoke(main),\n    ).Run()\n   },\n  },\n )\n}\n```\n\nAgain each command registers its flag by itself, so we have separation from other commands.\nSometimes we need to have shared flags between commands, then it is better to have them in config.\nFor the later case, `koanf` can help us with the structure as below:\n\n```go\nfunc Register(fs *pflag.FlagSet) {\n fs.StringP(\n  \"url\", \"u\",\n  nats.DefaultURL,\n  fmt.Sprintf(\"nats server url(s) e.g. %s\", nats.DefaultURL),\n )\n}\n```\n\nThis function register shared flags, and then we load configuration based on them with the following function:\n\n```go\nk := koanf.New(\".\")\n\nif err := k.Load(posflag.Provider(fs, \".\", k), nil); err != nil {\n log.Errorf(\"error loading config.yml: %s\", err)\n}\n\nif err := k.Unmarshal(\"\", \u0026instance); err != nil {\n log.Fatalf(\"error unmarshalling config: %s\", err)\n}\n\n```\n\n### Configuration\n\nThe main part of each application is its configuration. There are many ways for having configuration in the project from configuration file to environment variables.\n[Koanf](https://github.com/knadh/koanf) has all of them in a one beautiful package. The main points here are:\n\n- Having a defined and typed structure for configuration\n- Don't use global configuration. each module has its configuration defined in `config` module and it will pass to it in its initiation.\n- Print loaded configuration at startup, so everyone can validate the applied configuration.\n\nP.S. [koanf](https://github.com/knadh/koanf) is way better than [viper](https://github.com/spf13/viper) for having typed configuration.\nBy typed configuration I mean you have a defined structure for configuration and then load configuration from many sources into it.\n\nFor installing [koanf](https://github.com/knadh/koanf) you can use the following commands:\n\n```bash\ngo get -u github.com/knadh/koanf/v2\n\ngo get -u github.com/knadh/koanf/providers/file\ngo get -u github.com/knadh/koanf/providers/env\ngo get -u github.com/knadh/koanf/providers/structs\n\ngo get -u github.com/knadh/koanf/parsers/toml\n```\n\n### Infra vs Domain\n\nPackages and services that are defined in `domain` package only uses other packages from `domain` without using any\n3rd party packages. These packages and services specifies the core domain concepts.\n\n### Database\n\nThere is a `db` package that is responsible for connecting to the database. This package uses the database configuration that is defined in `config` module and create a database instance.\nIt is a good idea to ping your database here to have fully confident to your database instance before going forward.\nAlso for having an insight at database health you can call this ping function periodically and report its result with metrics (which I didn't do here).\n\n### Model\n\nProject models are defined in `model` package. These models are used internally but the can be used in `response` or `request` package.\nThere is no structure for communicating with database in this package.\n\n### Repository\n\nRepositories are responsible for commnunicating with database to store or retrieve models. Repositories are `interface` and there is an concrete and mocked implementation for them.\nconcrete implementation is used in main code and mocked one is used for tests. Please note that the tests for repositories are touchy and are done with actual database.\n\n### Handler\n\nHTTP handler are defined in `handler` package. [Echo](https://github.com/labstack/echo)\nis an awesome HTTP framework that has eveything you need.\nEach handler has its structure with a `Register` method that registers its route into a given route group.\nRoute group is a concept from [Echo](https://github.com/labstack/echo) framework for grouping routes under a specific parent path.\nEach handler has what it needs into its structure. Handler structure are created in `main.go` then register on their group.\n\n```go\ntype Healthz struct {}\n\n// Handle shows server is up and running.\nfunc (h Healthz) Handle(c echo.Context) error {\n return c.NoContent(http.StatusNoContent)\n}\n\n// Register registers the routes of healthz handler on given echo group.\nfunc (h Healthz) Register(g *echo.Group) {\n g.GET(\"/healthz\", h.Handle)\n}\n```\n\n### Metrics\n\nAll metrics are gathered using [Prometheus](https://prometheus.io/)\nbased on [open-telemetry](https://github.com/open-telemetry/opentelemetry-go).\nEach package has its `metric.go` that defines a structure contains the metrics and have methods for changing them.\nFor migrating from Prometheus to another service you just need to change `telemetry`.\nMetrics aren't global and they created for each instance seperately thanks to Open Telemetry design.\nFor having better controller on metrics endpoint there is another HTTP server that is defined in `telemetry`\npackage for monitoring.\n\n### Request/Response\n\nIt is good to have separated packages for requests and responses. These packages also contain validation logic.\nOne of the good validation pakcages in Go is [ozzo-validator](https://github.com/go-ozzo/ozzo-validation).\nAfter providing validate method, after getting request you can validate it with its method with ease.\n\n### Logging\n\nLogging one the most important part of application. At the beginning there is no need to have something more than simple stdout logs.\nBut in the future you need to strcuture you logs and ship them into an aggregation system because when your system grows detecting issues\nfrom text logs will be inpossible.\n\n[zap](https://github.com/uber-go/zap) is one the best logger for structure logging.\n`zap` forces you to pass it into your child module and you also name loggers with `Named` method.\nBy using the named logger you can easily find you module logs in your log aggregator.\n\n## Up and Running\n\nThis project only requires MongoDB, and you can run it with provided `docker-compose`.\n\n```bash\ncd deployments \u0026\u0026 docker-compose up -d\ncd cmd/koochooloo/ \u0026\u0026 go build \u0026\u0026 ./koochooloo\n```\n\n```bash\ncurl -X POST -d '{\"url\": \"https://elahe-dastan.github.io\"}' -H 'Content-Type: application/json' 127.0.0.1:1378/api/urls\ncurl -L 127.0.0.1:1378/api/CKaniA\n```\n\n## Load Testing\n\n```\n    checks.....................: 99.83% ✓ 2995  ✗ 5\n    data_received..............: 2.0 MB 64 kB/s\n    data_sent..................: 521 kB 17 kB/s\n    group_duration.............: avg=649.18ms min=153.18µs med=265.45ms max=30.95s   p(90)=1.61s    p(95)=2.06s\n    http_req_blocked...........: avg=14.12ms  min=0s       med=3µs      max=1.65s    p(90)=13µs     p(95)=147.04µs\n    http_req_connecting........: avg=6.23ms   min=0s       med=0s       max=1.36s    p(90)=0s       p(95)=0s\n    http_req_duration..........: avg=272.98ms min=0s       med=127.99ms max=4.81s    p(90)=830.93ms p(95)=1.29s\n    http_req_receiving.........: avg=125.23µs min=0s       med=60µs     max=11.21ms  p(90)=228µs    p(95)=363µs\n    http_req_sending...........: avg=50.78µs  min=0s       med=22µs     max=7.28ms   p(90)=86µs     p(95)=138µs\n    http_req_tls_handshaking...: avg=7.86ms   min=0s       med=0s       max=653.63ms p(90)=0s       p(95)=0s\n    http_req_waiting...........: avg=272.8ms  min=0s       med=127.71ms max=4.81s    p(90)=830.87ms p(95)=1.29s\n    http_reqs..................: 4000   129.093962/s\n    iteration_duration.........: avg=1.29s    min=142.34ms med=1.04s    max=30.97s   p(90)=2.18s    p(95)=2.64s\n    iterations.................: 1000   32.273491/s\n    vus........................: 100    min=100 max=100\n    vus_max....................: 100    min=100 max=100\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1995parham%2Fkoochooloo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F1995parham%2Fkoochooloo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F1995parham%2Fkoochooloo/lists"}