protected AppletChannel(Guid appletId, IAppInfo appInfo)
        {
            _appInfo = appInfo ?? throw new ArgumentNullException(nameof(appInfo));
            if (false == appInfo.IsAppletId(appletId))
            {
                throw new ArgumentException($"Invalid applet ID. Application: {appInfo.ApplicationName}, Applet ID: {appletId}");
            }
            AppletId = appletId;
            Instance = Guid.NewGuid();

            var disposing = _cancellationSource.Token.ToObservable();

            _privateResponses = Observable
                                .Defer(CreatePrivateResponsesObservable)
                                .TakeUntil(disposing)
                                .ObserveOn(_privateEventLoopScheduler)
                                .Do(InspectPrivateResponse)
                                .Do(args => Pulse())
                                .Where(IsValidResponse)
                                .Publish();

            _privateResponses
            .SubscribeOn(_privateEventLoopScheduler)
            .Where(response => _privateResponseObserversByCorrelationId.ContainsKey(response.CorrelationId))
            .Subscribe(
                OnPrivateResponse,
                OnPrivateResponsesError,
                OnPrivateResponsesCompleted);

            Observable
            .Interval(_appInfo.HeartbeatInterval)
            .TakeUntil(disposing)
            .SkipUntil(_pulse)
            .Select(CreateHeartbeatDto)
            .Select(ToDispatchArgs)
            .Subscribe(args =>
            {
                args.IntentId = AppInfo.HeartbeatIntent;
                SendAsync(args);
            });
        }