{"id":19339405,"url":"https://github.com/appfolio/seams","last_synced_at":"2025-11-16T20:02:03.984Z","repository":{"id":44610187,"uuid":"176835967","full_name":"appfolio/seams","owner":"appfolio","description":"Identify seams in your database","archived":false,"fork":false,"pushed_at":"2022-02-04T18:29:50.000Z","size":18,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-02-24T08:25:25.913Z","etag":null,"topics":["ddd","sql"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/appfolio.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}},"created_at":"2019-03-21T00:04:22.000Z","updated_at":"2022-02-04T18:29:51.000Z","dependencies_parsed_at":"2022-08-28T16:22:00.437Z","dependency_job_id":null,"html_url":"https://github.com/appfolio/seams","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/appfolio/seams","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appfolio%2Fseams","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appfolio%2Fseams/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appfolio%2Fseams/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appfolio%2Fseams/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/appfolio","download_url":"https://codeload.github.com/appfolio/seams/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/appfolio%2Fseams/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":284767947,"owners_count":27060132,"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","status":"online","status_checked_at":"2025-11-16T02:00:05.974Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["ddd","sql"],"created_at":"2024-11-10T03:21:59.685Z","updated_at":"2025-11-16T20:02:03.964Z","avatar_url":"https://github.com/appfolio.png","language":"Ruby","readme":"# seams\n\nBreak up your monolith by identifying [seams](http://www.informit.com/articles/article.aspx?p=359417\u0026seqNum=2) in your database schema!\n\n## Problem statement\n\nWhy would you need this?\n\nIf you have a large monolith application backed by a complex database schema, you might want to break it apart. In order to break it apart, you first need to identify seams that can become service boundaries.\n\nUsing [Domain-Driven Design (DDD)](http://dddcommunity.org/learning-ddd/what_is_ddd/), you might want to break out the \"product\" [bounded context](https://www.martinfowler.com/bliki/BoundedContext.html) or the \"marketing\" bounded context, or what have you. However, it can be hard to wrap your head around the entire database schema, not to mention the whole codebase.\n\nYou might look at this problem from a code-first perspective, or a database-first perspective.\nThis tool only considers the latter.\nFor a code-first perspective, check out Shopify's [ideas](https://engineering.shopify.com/blogs/engineering/deconstructing-monolith-designing-software-maximizes-developer-productivity) (and hopefully open-source tools in the future).\n\nFormal problem statement:\n\n    Given a database schema with a number of tables and foreign key constraints,\n    pick a subset of tables `initial_set` from the schema, then expand the subset to `min_set`.\n    A solution for `min_set` should contain `initial_set` and satisfy all foreign key constraints.\n\nThis tool allows you to explore your database schema and `find` one seam, or `solve` for all the seams.\n\n## System requirements\n\nIt doesn't matter which language your application is written in.\nHowever, it *does* matter which database you are using.\nSeams uses the ANSI SQL [information schema](https://en.wikipedia.org/wiki/Information_schema).\n\nThe current implementation is using the `mysql2` gem and has been verified for MySQL Server 5.6 and 5.7.\nI believe the code should be portable enough, but this is the \"Minimum Viable Product\": just one file of Ruby code.\n\n## Usage\n\nThis `irb` session should explain how to use it:\n```\nrequire './seams'\n# pass a set of MySQL options\nseams = Seams.new(database: \"yourdb\", username: \"youruser\")\ninitial_set = Set.new([\"users\", \"products\"])\nseams.find(initial_set)\nseams.solve # returns set of sets that are separated by seams\n```\n\nFurther explorations:\n```\n# debug output shows algorithm in action\nseams = Seams.new(database: \"yourdb\", username: \"youruser\", debug: true)\nseams.methods.sort - Object.methods # show available public methods\nseams.find(Set.new) # should return null set\nall_tables = seams.list_tables # print entire schema\nseams.find(all_tables) # should return entire schema\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappfolio%2Fseams","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fappfolio%2Fseams","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fappfolio%2Fseams/lists"}