{"id":17677174,"url":"https://github.com/toobeeh/valmar","last_synced_at":"2026-05-04T01:35:56.822Z","repository":{"id":213854005,"uuid":"735087096","full_name":"toobeeh/Valmar","owner":"toobeeh","description":"Persistance and domain layer for skribbltypo with gRPC API \u0026 EFCore ","archived":false,"fork":false,"pushed_at":"2026-02-15T14:33:35.000Z","size":888,"stargazers_count":1,"open_issues_count":15,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-15T18:51:00.155Z","etag":null,"topics":["docker","dotnet","grpc","skribbl-typo"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/toobeeh.png","metadata":{"files":{"readme":"Readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-12-23T16:07:07.000Z","updated_at":"2026-02-15T14:33:38.000Z","dependencies_parsed_at":"2023-12-23T18:27:02.478Z","dependency_job_id":"8d261564-0732-4a4c-a3b1-8166f04b98e2","html_url":"https://github.com/toobeeh/Valmar","commit_stats":null,"previous_names":["toobeeh/valmar"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/toobeeh/Valmar","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toobeeh%2FValmar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toobeeh%2FValmar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toobeeh%2FValmar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toobeeh%2FValmar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/toobeeh","download_url":"https://codeload.github.com/toobeeh/Valmar/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/toobeeh%2FValmar/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32591603,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-03T22:12:39.696Z","status":"ssl_error","status_checked_at":"2026-05-03T22:09:10.534Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["docker","dotnet","grpc","skribbl-typo"],"created_at":"2024-10-24T07:28:03.109Z","updated_at":"2026-05-04T01:35:56.806Z","avatar_url":"https://github.com/toobeeh.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Valmar\n[![part of Typo ecosystem](https://img.shields.io/badge/Typo%20ecosystem-Valmar-blue?style=flat\u0026logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAACV0lEQVR4nO3dPUrDYByA8UQ8g15AI+gsOOnmrufoIBT0DAUFB+/R3bFTobOCwQvoJSouNcObhHyZ9n2eHwiirW3Th79J2iaJJEmSJEmSJIC06iGu1+vgz9M0Df9CY6t8PkP2fMrYDADOAOAMAM4A4OrWGl3bj0Pp8+wEgDMAuP2uD//w7I6+DEf19fbc6eadAHAGAGcAcAYAZwBwnbcCTrIj+jL8Fx/55yA34wSAMwA4A4AzADgDgDMAOAOAMwC4zjuCzi+uN9+fZgeNrvuefw+69FfL10H/fgycAHAGAGcAcAYAZwBwnbcCioZeq2+quIVS5NbBHycAnAHARffRsOksr71Ml38Bi/mk9XVH5EfDFGYAcHVbAWWjw08NbyePEaRmDADOAOAMAM4A4Fq9FjCd5cG1zaeHrPeleXnzsvl+MZ802vooe4fSatn9ftUILp/iYxlCm51UTgA4A4Dr9eXgsv3wtJdfhx71fXICwBkAXGUAv+cLCH0pHk4AOAOAMwA4A4AzALhedwRpXBVneSu9X04AOAOAMwA4A4AzADgDgDMAOAOAMwA4A4AzADgDgDMAOAOAMwA4A4AzALio3xG0bUcu3UZOADgDgDMAOAOAMwC4qLcCRjxG0M5wAsAZAJwBwBkAnAHAGQCcAcAZAJwBwBkAnAHA+Y4gOCcAnAHAGQCcAcAZAFyrrYDH++NGl7+6ZZ0yZpc4AeAMAC66HUFDnLwyZk4AOAOAKz+QfMXx58dScdz7se5o8A7t0HJzAtAZAJwBwBkAnAFIkiRJkiRJUtySJPkBweNXgRaWkYQAAAAASUVORK5CYII=)](https://github.com/topics/skribbl-typo)\n\u003e Valmar, the capital of the god-like Valar, held the key to Middle-earth's destiny.  \n\nSimilar to its role in the LOTR lore, Valmar aims to be the centralized domain service for all skribbl-typo services.  \nValmar implements the persistance and domain-service layer, and exposes all functions with a gRPC API.  \nOther services can scaffold clients and types easily based on the proto definitions.   \nThis way, there is only one place where logic of new features needs to be implemented.\n\n## Brief ecosystem overview\nFollowing graphic illustrates the current state of the typo ecosystem:\n  \n![typo ecosystem](https://i.imgur.com/JN5AoEM.jpg)  \n  \nIt can be seen that domain/persistance layer is implemented multiple times; this is unnecessarily duplicated code resulting in unmaintainable code, higher difficulty to implement new features uniformly, and makes it incredibly easy to let errors slip in one of the many implementations.  \nValmar will act in future as the only implementation of domain \u0026 persistance layer, like it is currently done in Tirith. More in the Roadmap section.\n\n## Status \u0026 Roadmap\nValmar is has just left beta testing and is already used in production!  \nThe Nest API in toobeeh/Tirith has been refactored and solely uses Valmar instead of native database access.  \nValmar is currently deployed on the same server as the remaining typo ecosystem and not publicy exposed, since it has no (and will never have any) sort of authentication. \n\nFurther steps involve integrating more heavy business logic from Palantir into Valmar, and begin a separation of the \ndifferent Palantir components into separate services, each independent of another and receiving data from Valmar.  \nAnother goal is to get rid of the domain code from Ithil-Rebirth as well and fully switch to Valmar.\n\n## Architecture\nValmar aims to improve the Domain Driven Design architecture in the typo ecosystem.  \nWhile as a whole, it implements the Domain/Business and Persistence layer in the ecosystem, Valmar itself is structured in individual layers.  \n\n- gRPC Services / Application Layer:  \nThe services in /Grpc implement the services in the proto definitions.  \nThey must not include domain-specific functions; just retrieve data from underlying layers and map them to response objects.\n- Domain Services / Service Layer:  \nThe services in /Domain implement the domain logic and access data from the persistence layer.  \nDomain services should be designed to enable the application layer to re-use fetched data to avoid fetching the same data multiple times.  \nTherefore, adding parameters - primitives or domain objects - to the service functions is encouraged, instead fetching an object from another service in the service method.\n- Persistence Layer:  \nThe persistence layer is scaffolded by efcore tools. \n\nAll layers use dependency injection of mappers, database, loggers and other service dependencies.\n\n## Design guidelines\n\n### Performance\nEach service should have the goal to create granular access to its entities and not waste resources to calculate/fetch data that might not be needed by the majority of its consumers.  \nGenerally speaking, Valmar should not cache any data.\nAn exception are drops, which are the main performance bottleneck of the whole application. \n\nFollowing concept should be implemented to handle drops:\n### Drop Chunks with Octree access\nThe drop table is a huge table with a lot of data (a couple millions of drops).\nTo handle this, the table should be split into chunks.\nThe chunks should be stored in an octree structure, where each chunk is a leaf node in the tree.\nEach node in the octree is either an abstraction of its child nodes, or a leaf node which is assigned to a range of drop IDs in the database.   \n\nThe abstraction nodes calculate the stats of their children and store (cache) that, to save performance cost.  \nInitially, abstraction nodes are dirty. When a read request is made, the node calculates the stats (recursively) from its child nodes, saves them, marks itself as clean, and returns the value.  \nFor the next read operation, the node can directly return the stats without recalculating them.  \nWhen a node encounters a writing access, it marks the stats as dirty, which is propagated downwards to the chunks/nodes that are requried for the write access.\nAt the next read access, the dirty nodes will have to recalculate the stats from its child nodes.\n\nThe leaf nodes are assigned to a range of drop IDs and calculate their stats directly from the database.\nDuring the initialization of the octree, the initializer creates a parent abstraction node and starts loading chunks of size n.\nIt adds the chunks to the current head node; when the head node is filled up with eight chunks it moves the chunks to a new child node and therefore has seven new chunks available. \n\nThis uses the temporal locality of drop accesses, especially the write accesses - new drops and redeeming league drops. \nMost chunks will remain clean and when frequent write accesses occur, it is likely that they are affecting a number of chunks in the same area of the octree due to drop events.\nThe chunksize should be chosen to be as large as possible to minimize the amount of nodes in the octree, but as small as necessary to keep the amount of dirty nodes low.\n\n### Entities / DTOs\nFollowing layers should expose following entity types:\n- Application layer: GRPC response classes\n- Domain Layer: Entities, JSON Models for partial entities or \"Domain Data Objects\"/Ddos.\n\ngRPC requests/responses have to be only used in the application layer.  \nIf suitable JSON Models exists, they may be used instead of creating dedicated Ddo classes.\nDdo classes should be used to combine data from multiple domains to a single response.\n\nThe application layer should only use Automapper to convert Domain layer entities to Application layer entities.   \nThis engages that the domain layer produces ready-to-use entities and decouples it from the application layer.  \nThe primary objective of the auto-mapping should be stripping properties and mapping to slightly different structure.\n\nThe domain layer should not use Automapper to deal more explicitly with attribute name/type conversion.\nAutomapper is useful for converting objects with similar signature, but the domain layer should rather produce full-featured objects than \"stripped\" objects.\n\n### Exception handling \ngRPC exceptions should be only thrown in the application layer.  \nExceptions that occur during domain logic should use exception classes located in /Domain/Exceptions.  \nThey will be mapped to gRPC exceptions with appropriate status code in the exception interceptor.  \n\nIn most of the cases, it is not needed to throw exceptions in the application layer at all.  \nDomain exceptions are handled in the domain layer, and static validation is performed via the validator configuration.  \n\n### Interfaces for development  \nSince writing tests (and TDD) for this project is out of scope for a one-man-team, interfaces are used to guideline feature implementation.  \n- Proto definitions should be the first step to start a new feature. This ensures consideration of the client's needs.  \n- Considering the proto definitions, an interface for a new domain service (or to extend an existing) should be created to satisfy needed functions to make the service work.\n- The gRPC service class can be created, depending on the new domain service interface. If the interface is insufficient, extend it.\n- The domain service can now be implemented and added to the dependency injection. \n- Mappers and validators should be written and added to dependency injection.\n\nThis workflow ensures a design process where the domain logic follows last - preventing possibly lacking implementations - and a clean implementation chain where the project is able to compile at any time.\n\n## Database\nValmar accesses the MariaDb Palantir Database. \n### Database client scaffolding\n\nUsing the entity framework and its cli tools, the persistence layer can be easily scaffolded:  \n`dotnet ef dbcontext scaffold \"Name=ConnectionStrings:Palantir\" Pomelo.EntityFrameworkCore.MySql -o Database --force --data-annotations`  \n\n## Refactoring TODOs\n- rename all proto messages to \"Message\" instead of \"Request\" or \"Reply\"\n- make individual request messages for each rpc call\n- review enum naming (should include message suffix)\n- review usage of service parameters; should take objects instead of fetching them\n- add real persistence layer with repositories (? sos)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoobeeh%2Fvalmar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftoobeeh%2Fvalmar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftoobeeh%2Fvalmar/lists"}