refactor(architecture): simplify config and ports - #66
Merged
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #66 +/- ##
==========================================
+ Coverage 80.45% 82.16% +1.71%
==========================================
Files 29 27 -2
Lines 711 673 -38
==========================================
- Hits 572 553 -19
+ Misses 96 77 -19
Partials 43 43 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR simplifies configuration flow and dependency boundaries without changing the CLI or generated TOC format.
internal/appthe composition root for use cases;internal/utilstointernal/adapters;RemotePosterwrapper layer;FileWriter.Motivation
Before this change,
app.Configwas copied almost field-for-field intocontroller.Config, then copied again intousecase/config.Config. Most of those fields were unrelated to controller execution and were not used by the individual use cases:RemoteMdstored the complete use case config but did not read any field from it;LocalMdandRemoteHTMLonly needed theDebugvalue;app.Runused them.The central
internal/core/portspackage also made interfaces provider-oriented. Every consumer depended on the same shared package even when it needed only one or two methods. In addition,RemotePosterwrapped anotherRemotePosterimplementation without adding behavior, andinternal/utilsmixed HTTP transport, presentation formatting, and heading cleanup.Configuration changes
app.Configremains the top-level application configuration, but its fields are now grouped by responsibility:Files,Serial,DebugPresentationConfigGitHubConfigtoc.ConfigThe existing GitHub field names
GHToken,GHUrl, andGHVersionare preserved insideGitHubConfig.cmd/gh-md-tocnow creates these groups directly while parsing CLI flags. Adding a presentation or renderer flag no longer requires adding the same field to controller and use case configuration types.app.Newnow performs the dependency wiring directly:LocalMd,RemoteMd, andRemoteHTMLwith only their required settings and dependencies;The absolute-link rule is preserved.
app.Newstill enablestoc.Config.AbsolutePathswhen at least one document path was supplied through the CLI and leaves it disabled for stdin.Controller and use case configuration
controller.Confignow contains only:Files, used to select file processing or stdin processing;Serial, used to select the worker count.The former
controller.Config.ToUseCaseConfigconversion and the completeinternal/core/usecase/configpackage are removed.Use cases now receive only the settings they use:
LocalMdreceivesdebug boolbecause it controls the optional HTML debug artifact;RemoteHTMLreceivesdebug boolbecause it controls the optional JSON debug artifact;RemoteMdreceives no configuration value because it did not use one.The former aggregate
usecase.Newconstructor is removed. It existed mainly to forward the same broad config and dependency list to the concrete use cases. Construction now happens inapp.New, which is the application composition root.Consumer-owned interfaces
The central
internal/core/portspackage is removed. Small interfaces are declared next to their consumers instead:useCaseandloggercontracts;LocalMdowns file checker, file writer, HTML converter, TOC grabber, and logger contracts;RemoteMdowns remote getter, Markdown processor, temporary file, and logger contracts;RemoteHTMLowns remote getter, TOC grabber, temporary file, and logger contracts;HTMLConverterowns the remote poster contract;All dependencies still satisfy these interfaces implicitly. The change removes the shared interface package without coupling core code to adapter implementations.
RemoteMdnow depends on a small Markdown processor interface instead of the concrete*localmd.LocalMdtype. This keeps its orchestration behavior unchanged while removing an unnecessary concrete dependency between use case packages.HTTP infrastructure
HTTP helpers and HTTP-specific error types are moved from
internal/utilstointernal/adapters/http.go, next toRemoteGetter,RemotePoster, andHTMLConverter.The existing helper names and behavior are preserved:
HttpGet;HttpGetJson;HttpPost;HTTPStatusError;ResponseBodyTooLargeError.The moved implementation preserves:
http.Client;User-Agent, JSON, content type, and authorization headers;The HTTP tests move with the implementation into the adapters package so they continue to exercise the same behavior at its new location.
RemotePoster simplification
Previously the flow was:
RemotePosterdid not add behavior. It only delegated every call to the nestedports.RemotePoster.The new flow is:
RemotePosternow stores the injected*http.Clientdirectly.realPosterandNewRemotePosterXare removed. The existingNewRemotePosterandNewRemotePosterWithClientconstructors remain.The test seam used by
HTMLConverterremains available throughNewHTMLConverterX, which now accepts the consumer-ownedremotePosterinterface.Presentation formatting
ShowHeaderandShowFootermove frominternal/utilstointernal/app, where their output is controlled and written byApp.Run.Their names and exact output are preserved. The footer is still omitted after a processing error, and the header is still omitted for stdin and multi-file input according to the existing behavior.
internal/utilsnow contains only the shared heading text cleanup used by the two extractor adapters.Removed pass-through code
The following files are removed because their responsibilities no longer exist:
internal/core/ports/ports.go;internal/core/usecase/config/config.go;internal/core/usecase/new.go;internal/app/config_test.go, which only tested the deleted field-for-field conversion methods.FileWriteralso no longer stores or receives a logger because it never used that dependency.Compatibility
This is an internal architecture refactoring. It does not change:
Tests
The following checks pass:
GOTOOLCHAIN=go1.21.13 go test ./...;GOTOOLCHAIN=go1.26.5 go test -race ./...;go vet ./...;golangci-lint run ./...;go mod tidy -diff;go mod verify;make e2e.The live e2e suite covers all nine existing scenarios: