I keep each service in it's own solution (and repo) and bring things together into a single application only at deployment time. Build and deployment occurs via PowerShell which can be triggered as part of a CI/CD process. Only the updated services are modified and I have it updating the manifest versions automatically using the current date/time.
For service that have other service dependencies, I've references their project within the solution so I can easily debug, but only the primary service is deployed.
Adding more detail to the answer, the comment block is too small.
I created a directory to place the release versions in the correct SF directory structure. I copy the ApplicationManifest.xml for the release to that directory, though you could make this a repo and check just that file in. I have a buildrelease.ps1 that enumerates the repos and runs msbuild for the release configuration. For that configuration, I'm not building anything but the service to be deployed (no unit tests or other test apps for debugging purposes). The version is generated automatically based on the date and time, e.g. 2017_05_31_1702. This is what is updated in the service manifest and eventually in the application manifest. After a successful build, the service manifest is updated back in solution directory like this:
$serviceManifestPath = "$solutionDir\src\${ServiceName}Service\PackageRoot\ServiceManifest.xml"
Set-ItemProperty $serviceManifestPath -name IsReadOnly -value $false
$serviceManifestXml = [xml](Get-Content $serviceManifestPath)
$ns = New-Object System.Xml.XmlNamespaceManager($serviceManifestXml.NameTable)
$ns.AddNamespace("ns", $serviceManifestXml.DocumentElement.NamespaceURI)
$serviceManifestXml.ServiceManifest.Version = $NewVersion
$serviceManifestXml.ServiceManifest.CodePackage.Version = $NewVersion
$serviceManifestXml.ServiceManifest.ConfigPackage.Version = $NewVersion
$serviceManifestXml.Save($serviceManifestPath)
Next msbuild is called again to package the SFProj.
$buildResult = & $msBuild $sfproj /p:Configuration=Release /nologo /v:M /fl /flp:LogFile="msbuild.log;Verbosity=Normal" /nr:false /target:package
Today I'm only building services that have a modified service manifest, so you have to remember to modify the manifest when you modify code/config. I'd like to improve that, just not enough time.
After packaging, the solution's release{service name}Service.pkg is copied to the release folder. When the script is done, the resulting directory contains any changed services in the correct format.
Next script is a deployment script per cluster. I'm sure one could be created that is data driven. This connects to the remote cluster, tests the package, uploads the package to the image store and does a new or upgrade depending on if the application already exists. I have a version of this that can deploy to my one-box so I can test/debug all of the services running together all using local configuration.