public Program( IFileSystem fileSystem, IMetadataReader metadataReader, TextWriter outputWriter, CancellationTokenSource cancellationTokenSource) { /* Composition Root. Out of process resources can be swapped with fakes in tests. */ _outputWriter = outputWriter; _cancellationTokenSource = cancellationTokenSource; var report = new Report(_outputWriter); var cameraFilesFinder = new CameraFilesFinder(fileSystem); cameraFilesFinder.OnCameraFilesFound += report.HandleCameraFilesFound; var cameraFileFactory = new CameraFileFactory(); var cameraFileNameConverter = new CameraFileNameConverter( metadataReader, cameraFileFactory, fileSystem); var consoleOutput = new ConsoleOutput(_outputWriter); var cameraFileCopier = new CameraFileTransferer( cameraFileNameConverter, fileSystem, (s, d, o) => fileSystem.File.Copy(s, d, o)); cameraFileCopier.OnFileTransferred += (_, args) => consoleOutput.HandleFileCopied(args.sourceFile, args.destinationFile); AssignEventHandlersForCameraFileTransferer(cameraFileCopier); var internalCopyingOrchestrator = new Orchestrator( cameraFilesFinder, cameraFileCopier, _cancellationTokenSource.Token); internalCopyingOrchestrator.OnError += (_, args) => consoleOutput.HandleError(args.filePath, args.error); internalCopyingOrchestrator.OnError += (_, args) => report.AddErrorForFile(args.filePath, args.error); internalCopyingOrchestrator.OnException += (_, args) => consoleOutput.HandleException(args.filePath, args.exception); internalCopyingOrchestrator.OnException += (_, args) => report.AddExceptionForFile(args.filePath, args.exception); IOrchestrator copyingOrchestrator = new ReportingOrchestratorDecorator( internalCopyingOrchestrator, report); var cameraFileMover = new CameraFileTransferer( cameraFileNameConverter, fileSystem, (s, d, o) => fileSystem.File.Move(s, d, o)); cameraFileMover.OnFileTransferred += (_, args) => consoleOutput.HandleFileMoved(args.sourceFile, args.destinationFile); AssignEventHandlersForCameraFileTransferer(cameraFileMover); var internalMovingOrchestrator = new Orchestrator( cameraFilesFinder, cameraFileMover, _cancellationTokenSource.Token); internalMovingOrchestrator.OnError += (_, args) => consoleOutput.HandleError(args.filePath, args.error); internalMovingOrchestrator.OnError += (_, args) => report.AddErrorForFile(args.filePath, args.error); internalMovingOrchestrator.OnException += (_, args) => consoleOutput.HandleException(args.filePath, args.exception); internalMovingOrchestrator.OnException += (_, args) => report.AddExceptionForFile(args.filePath, args.exception); IOrchestrator movingOrchestrator = new ReportingOrchestratorDecorator( internalMovingOrchestrator, report); _copyCommand = new CopyCommand(args => copyingOrchestrator.Execute(args)); _moveCommand = new MoveCommand(args => movingOrchestrator.Execute(args)); var fileChecker = new FileChecker( cameraFilesFinder, metadataReader, cameraFileFactory, new Commands.Check.Output.ConsoleOutput(_outputWriter)); _checkCommand = new CheckCommand(args => fileChecker.Execute(args)); void AssignEventHandlersForCameraFileTransferer( CameraFileTransferer cameraFileTransferer) { cameraFileTransferer.OnDirectoryCreated += (_, directory) => consoleOutput.HandleCreatedDirectory(directory); cameraFileTransferer.OnFileSkipped += (_, args) => consoleOutput.HandleFileSkipped(args.sourceFile, args.destinationFile); cameraFileTransferer.OnFileSkipped += (_, args) => report.AddSkippedFile(args.sourceFile, args.destinationFile); cameraFileTransferer.OnFileTransferred += (_, args) => report.IncrementTransferred(args.dryRun); } }